GetObject 有时会生成新的 COM+/OLE 对象,而不是获取现有对象

GetObject sometimes spawns new COM+/OLE object instead of getting the existing one

从 Excel,我挂钩了一个 Glink OLE 对象 - 这个对象是一个 运行ning 程序,我需要与之交互,所以程序将(并且必须) 运行宁此代码之前已经打开。

由于(我假设)我无法访问的本地配置或某些我不理解的 API 行为,有时代码会创建一个新对象而不是 Get -ing 已经打开的那个。就其本身而言,这并不是最糟糕的事情 - 它可以解决。

如果我手动 运行 一个不包含任何循环或枚举尝试的 Sub,我可能会挂钩打开的对象(正确的)或相同的新的、不可用的对象生成类型 (false)。如果我挂错了一个,只需重新 运行 宁 Sub 给我正确的对象 - 这是一致的。

如果我 运行 一个 Sub 循环自身(试图复制手动重新 运行 子的行为),没有这样的运气 - 它给了我"same" 对象(实际上不是同一个对象 - 它每次都会产生一个新实例,但无论如何这永远不会是正确的对象,因为正确的对象 已经存在 )及以上。

我有一些代码:

Dim gl As Glink.Auto
Set gl = Nothing
Set gl = GetObject("", "Glink.Auto")

这在上面第 2 段中概述了 - 它会可靠地在我不需要/不能使用的对象和我确实想要的对象之间切换,因此发现正确的对象是微不足道的根据第一个 运行 的结果,运行 对宏执行 1 次或 2 次。

有两种方法可以区分我是否得到了正确的对象:第一种是检查Instance 属性,所以我的代码包括检查:

Debug.Print gl.Instance

如果我有正确的对象,Instance 将是 0。相反,如果 Instance 不是 0,我得到的东西无法使用。

第二个是Automated。如果这是 TRUE,则该对象是通过 OLE 生成的(因此对我来说无法使用)。反之,如果是FALSE,我知道它指的是我手动打开的对象(这就是我想要的对象)。

最终代码

strict 设置为 TRUE 可启用循环。手动 运行s 和程序循环 运行s 在代码上没有区别,除了标记为手动的 运行s 有 strict = False 而其他有 strict = True

' Glink globals
Global gl As glink.Auto
Global scr As IAutoScreen

' Other globals
Global dbg As Boolean
Global strict As Boolean

Sub Glink_Initialize()

    Dim retryCount As Integer
    retryCount = 0

    dbg = True
    strict = False

    ' Start GLINK session hook
Retry:
    Set gl = Nothing
    Set gl = GetObject("", "Glink.Auto")

    If dbg = True Then
        If Err.Number <> 0 Then
            GoTo Terminate:
        Else
            If gl.Instance <> 0 And strict = True Then
                If retryCount < 5 Then
                    If dbg = True Then
                        Debug.Print "No valid instance found, retrying."
                        Debug.Print "Instance: " & gl.Instance & " RetryCount: " & retryCount & vbCr
                    End If
                    retryCount = retryCount + 1
                    GoTo Retry:
                ElseIf strict = True Then
                    If dbg = True Then Debug.Print "RetryCount exceeded limit"
                    GoTo Terminate:
                End If
            End If

            Debug.Print "Image: " & gl.Caption & vbCr & "Instance: " & gl.Instance & vbCr & "Automation: " & gl.Automated
        End If
    End If
    ' End GLINK session hook

    Exit Sub
Terminate:
    Debug.Print "No Glink detected! Terminating."
    Set gl = Nothing
End Sub

下面是两个手册 运行 中的一些调试语句(无循环)紧接着

' Run 1
Image: GLINK - (ref.:2242.2853:ErgoIntegration)
Instance: 49     ' <-- Unusable object
Automation: True ' <-- Spawned by OLE

' Run 2
Image: GLINK - Vegard - AA90        
Instance: 0       ' <-- Usable object
Automation: False ' <-- Not spawned by OLE = this is the object I want

这个过程以绝对的一致性重复着令人作呕的过程。获取正确的对象,生成新对象,获取正确的对象,生成新对象等:

Image: GLINK - (ref.:2242.2853:ErgoIntegration)
Instance: 56
Automation: True

Image: GLINK - Vegard - AA90
Instance: 0
Automation: False

Image: GLINK - (ref.:2242.2853:ErgoIntegration)
Instance: 57
Automation: True

Image: GLINK - Vegard - AA90
Instance: 0
Automation: False

Image: GLINK - (ref.:2242.2853:ErgoIntegration)
Instance: 58
Automation: True

Image: GLINK - Vegard - AA90
Instance: 0
Automation: False

这是包含循环

的代码的 1 运行 中的调试语句
No valid instance found, retrying.
Instance: 43 RetryCount: 0

No valid instance found, retrying.
Instance: 44 RetryCount: 1

No valid instance found, retrying.
Instance: 45 RetryCount: 2

No valid instance found, retrying.
Instance: 46 RetryCount: 3

No valid instance found, retrying.
Instance: 47 RetryCount: 4

所以似乎使用编程循环跳过了获取正确对象的步骤...为什么?有办法解决这个问题吗?

根据@Flephal 的评论和 MSDN,人们会认为省略 GetObject 的第一个参数是正确的方法。出于某种原因,我并没有始终如一地遇到这种情况 - 如果第一个参数不存在,有时我会收到 429 错误。

更具体地说,这是 MSDN 的摘录:

If pathname is a zero-length string (""), GetObject returns a new object instance of the specified type. If the pathname argument is omitted, GetObject returns a currently active object of the specified type. If no object of the specified type exists, an error occurs.

根据此描述,我假设我收到了引用错误,因为未找到指定的对象(但我正在查看它的 window,所以我知道它在那里)。我不知道为什么会这样,但是:

解决方法

改变这个:

Set gl = Nothing
Set gl = GetObject("", "Glink.Auto")

对此:

Set gl = Nothing
Set gl = GlinkObjectHook

并添加此功能:

Function GlinkObjectHook() As glink.Auto
    Dim glink As glink.Auto
    On Error Resume Next
    Set glink = GetObject(, "Glink.Auto")
    On Error GoTo 0
    If glink Is Nothing Then Set glink = GetObject("", "Glink.Auto")
    Set GlinkObjectHook = glink
End Function