【问题标题】:How to pass & convert a VARIANT variable from one sub to an OBJECT variable in another sub?如何将 VARIANT 变量从一个 sub 传递和转换为另一个 sub 中的 OBJECT 变量?
【发布时间】:2021-04-08 14:46:15
【问题描述】:

我正在为我的 VBA 代码而苦苦挣扎。我有两个潜艇:

  • 第一个 Sub 标识并弹出 Internet Explorer 窗口,我的抓取宏将在该窗口上运行。这个 Sub (IEGetActivePage) 工作正常;
  • 第二个 Sub 将执行抓取工作。这个 Sub 需要使用弹出 Internet Explorer 窗口的对象变量。

我的目标是从第一个 Sub (IEGetActivePage) 调用 VARIANT 变量 IE_Title 到下一个 Sub (TestScrape2) 并将其转换到一个 OBJECT 变量(以便 TestScrape2 中的代码工作)。


编辑:

  • SET函数似乎不起作用。我得到相同的结果:Internet Explorer 窗口弹出,但仅此而已。甚至没有用于检查变量类型的 MsgBox。

这是 IEGetActivePage 代码,带有 IE_Title 变量:

Option Explicit

Sub IEGetActivePage()

Dim marker As Variant  
Dim IE_Window As Variant
Dim IE_count As Variant
Dim X As Variant
Dim IE_Title As Variant   'The web page used in this code is the following: https://fr.wikipedia.org/wiki/Visual_Basic_for_Applications
Dim IE As Variant

marker = 0                                      
Set IE_Window = CreateObject("Shell.Application")
IE_count = IE_Window.Windows.Count                

For X = 0 To (IE_count - 1)               
    On Error GoTo ErrorHandler                    
    IE_Title = IE_Window.Windows(X).Document.Title
    If IE_Title Like "Visual Basic for Applications — Wikipédia" Then
        Set IE = Fenêtre.Windows(X)
        marker = 1
        Exit For
    Else
    End If
Next

If marker = 0 Then
    MsgBox ("The Internet window cannot be found")
Else
    AppActivate IE_Title 'Activates the correct Internet Explorer windows

'---Until there, the code works fine. The next step is an attempt to create a new variable out of the IE_Title variable and converting it to an object. As @Gustav suggested, I tried the SET function but I get the same result---

    Dim IEObject As Object
    Set IEObject = IE_Title.InternetExplorer

'---To check if the conversion worked, I inserted a MsgBox using the IsObject function below---
    
    Dim VariableCheck As Boolean
    If VariableCheck = IsObject(IEObject) Then
        MsgBox (VariableCheck)
    Else
    End If

'---As a result, the message box does appear. No errors displayed either. And the scraping evidently did not work. The only thing that works is the Internet Explorer window activation. So there must be an issue here---

    Call TestScrape2  'Calling the next Sub
End If

ErrorHandler:
    If Err.Number > 0 Then 'TODO: handle specific error
        Err.Clear
        Resume Next
    End If
End Sub

而下面的代码就是下一个Sub(TestScrape2):

Option Explicit

Sub TestScrape2()
    
    Dim IEObject As Object
    Set IEObject = New InternetExplorer

    IEObject.Visible = True
    Dim IEDocument As HTMLDocument
    Set IEDocument = IEObject.Document
        
    Debug.Print IEDocument.getElementbyId("firstHeading").innerText
        
End Sub

有什么想法吗?关于如何调用一个 Sub 中使用的变量并将其转换为另一个 Sub 中的对象变量?

希望这足够清楚

【问题讨论】:

  • 如果多个 subs 处理相同的数据,我喜欢使用module-level variables。这样您就不必将每条数据作为参数传递。它还可以帮助您避免变量与对象声明的问题。
  • @Toddleson 你的意思是不要像Dim IE_Title As Variant 那样声明变量,我应该使用Public IE_Title As Variant
  • @James69 模块级意味着您在任何子或函数之外声明变量。应用程序会存储这些值,直到它关闭或重置,这意味着您可以运行一个宏,获取一个值,然后稍后在相同或不同的宏中使用该值。在您的情况下,这意味着您可以快速轻松地使用多个 sub 引用您的数据,而无需将所有内容作为参数传递。

标签: vba object variables internet-explorer type-conversion


【解决方案1】:

尝试Set对象:

    Dim IEObject As Object
    Set IEObject = IE_Title.InternetExplorer

另外,检查如下:

    Dim VariableCheck As Boolean

    VariableCheck = IsObject(IEObject)
    MsgBox CStr(VariableCheck)

【讨论】:

  • 嗨@Gustav,感谢您的建议。我尝试使用你提到的set 函数,但结果是一样的:没有 MsgBox,没有错误。我使用 Rubberduck 检查了压痕,以确保那里什么都没有。只有第一个子触发并弹出 Internet Explorer 窗口。
  • 尝试强制打开消息框。
【解决方案2】:

消息框不显示,因为VariableCheck 为假而IsObject(IEObject) 为真。另外,IE_Title 是一个字符串,我认为Set IEObject = IE_Title.InternetExplorer 是错误的语法。

如果你想将一个变量从sub1传递给sub2,可以参考this answer

作为一种解决方法,您可以将 IE 变体从 sub1 传递到 sub2,然后获取 IE 的 url 并使用该 url 打开一个新的 Internet Explorer 窗口。您可以查看以下示例:

Option Explicit

Sub IEGetActivePage()

Dim marker As Variant
Dim IE_Window As Variant
Dim IE_count As Variant
Dim X As Variant
Dim IE_Title As Variant   'The web page used in this code is the following: https://fr.wikipedia.org/wiki/Visual_Basic_for_Applications
Dim IE As Variant

marker = 0
Set IE_Window = CreateObject("Shell.Application")
IE_count = IE_Window.Windows.Count

For X = 0 To (IE_count - 1)
    On Error GoTo ErrorHandler
    IE_Title = IE_Window.Windows(X).Document.Title
    If IE_Title Like "Visual Basic for Applications — Wikipédia" Then
        Set IE = IE_Window.Windows(X)
        marker = 1
        Exit For
    Else
    End If
Next

If marker = 0 Then
    MsgBox ("The Internet window cannot be found")
Else
    AppActivate IE_Title     
    TestScrape2 IE  'Calling the next Sub
End If

ErrorHandler:
    If Err.Number > 0 Then 'TODO: handle specific error
        Err.Clear
        Resume Next
    End If
End Sub

Sub TestScrape2(IE As Variant)
    
    Dim IEObject As Object
    Set IEObject = New InternetExplorer

    IEObject.Visible = True
    IEObject.navigate IE.LocationURL
    
    While IEObject.Busy
       DoEvents
    Wend
        
    Debug.Print IEObject.Document.getElementbyId("firstHeading").innerText
        
End Sub

【讨论】:

    【解决方案3】:

    因此,在组合了消息框的@Gustav、模块级变量的@Toddleson 和他的示例的@Yu Zhou 的答案后,下面是适合我的代码:

    Option Explicit
    Public IE_Title As Variant 'The web page used in this code is the following: https://fr.wikipedia.org/wiki/Visual_Basic_for_Applications
    Sub IEGetActivePage()
    
    Dim marker As Variant
    Dim IE_Window As Variant
    Dim IE_count As Variant
    Dim X As Variant
    Dim IE As Variant
    
    marker = 0
    Set IE_Window = CreateObject("Shell.Application")
    IE_count = IE_Window.Windows.Count
    
    For X = 0 To (IE_count - 1)
        On Error GoTo ErrorHandler
        IE_Title = IE_Window.Windows(X).Document.Title
        If IE_Title Like "Visual Basic for Applications — Wikipédia" Then
            Set IE = IE_Window.Windows(X)
            marker = 1
            Exit For
        Else
        End If
    Next
    
    If marker = 0 Then
        MsgBox ("The Internet window cannot be found")
    Else
        AppActivate IE_Title     
        TestScrape2 IE  'Calling the next Sub
    End If
    
    ErrorHandler:
        If Err.Number > 0 Then 'TODO: handle specific error
            Err.Clear
            Resume Next
        End If
    End Sub
    

    Sub TestScrape2(IE_Title As Variant)
        
        Dim IEObject As Object
        Set IEObject = New InternetExplorer
    
        IEObject.Visible = True
        IEObject.Navigate IE_Title.LocationURL
        
        Do While IEObject.Busy = True Or IEObject.ReadyState <> READYSTATE_COMPLETE
            Application.Wait Now + TimeValue("00:00:05")
        Loop
        
        Debug.Print IEObject.LocationURL
                
    End Sub
    

    以下是我与之前的代码相比所做的更改,以及关于此问题的假设结论:

    • IE_Title 声明为模块级变量(或公共变量) 在模块脚本的顶部,而不是仅在第一个 Sub 上声明它(如我的第一个草案)。这允许第二个 Sub 重用变量,而不会在变量类型声明中产生冲突。

    • 忘记了在同一个 Sub 中转换变量的想法。它可能适用于其他一些情况。也许高级用户可以让它工作,但我很高兴这个脚本能正常工作,哈哈。

    • 在第一个窗口之外创建一个重复的 Internet Explorer 窗口以使脚本正常工作。 @Yu Zhou 建议的这个简单的代码行IEObject.Navigate IE_Title.LocationURL 改变了游戏规则(尽管我保留了IE_Title 而不是IE,这不起作用)。由于第一个 Sub 标识了正确的 IE 窗口,它还存储了有关打开的窗口的属性信息,例如 URL,在这种情况下可以重复使用。

    • 我也意识到,虽然有一个重复的 IE 窗口可能是多余的,但脚本可以按原样工作,最后关闭重复的窗口并不是什么大问题。

    • 在第二个 Sub 中添加一个临时的 MsgBox,以确认 IE_Object 是一个对象变量。它没有显示在上面的代码中,但我在我的版本中添加了它们以检查没有任何东西阻止脚本。

    谢谢大家!

    N.B.:嘿,您是阅读本文的高级用户,如果您可以简化此脚本,请随时在此处分享 :-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      • 2023-03-11
      • 1970-01-01
      • 2021-09-28
      • 2018-10-22
      • 1970-01-01
      相关资源
      最近更新 更多