From Excel, I am hooking a Glink OLE object - this object is a running program that I need to interface with, so the program will (and must) already be open before running this code.
Due to either (I assume) local configurations that are out of my reach or some API behavior that I don't understand, sometimes the code will create a new object instead of Get
-ing the one that is already open. By itself, that isn't the worst thing - it can be worked around.
If I manually run a Sub
that doesn't contain any loops or attempts at enumeration, I might hook either the open object (correct) or a new, unusable object of the same type gets spawned (false). If I hook the wrong one, simply re-running the Sub
gives me the right object - this is consistent.
If I run a Sub
that loops over itself (attempting to replicate the behavior from manually re-running the sub), no such luck - it gives me the "same" object (not really the same object - it spawns a new instance every time, but at any rate this will never be the correct object because the correct one already exists) over and over.
I have some code:
Dim gl As Glink.Auto
Set gl = Nothing
Set gl = GetObject("", "Glink.Auto")
And this works as outlined in the 2nd paragraph above - it'll reliably alternate between some object I don't need / can't use and the object that I do want, so it's trivial to discover the right object by running the macro either 1 or 2 times depending on what the outcome of the first run is.
There are 2 ways to distinguish whether I've gotten the right object or not: the first is to examine the Instance
property, so my code includes a check:
Debug.Print gl.Instance
If I have the correct object, Instance
will be 0
. Conversely, if Instance
is anything other than 0
, I've been given something unusable.
The second is Automated
. If this is TRUE
, the object was spawned via OLE (and is thus unusable to me). Conversely, if it is FALSE
, I know it refers to the object I opened manually (and this is the object I want).
Final code
Setting strict
to TRUE
enables the loop. The manual runs and programmatic loop runs do not differ in code except for the runs marked as manual have strict = False
while the others have 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
Here are some debug statements from two manual runs (no loop) right after each other
' 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
This process repeats itself ad nauseum with absolute consistency. Get the right object, spawn new object, get the right object, spawn new object, etc:
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
Here are the debug statements from 1 run of code containing the loop
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
So it seems that using a programmatic loop skips the step of getting to the correct object... why? Is there a way to fix this?
As per @Flephal's comment and MSDN, one would think that omitting the first argument to GetObject
would be the correct way to do it. For some reason, I do not experience this consistently - sometimes I get a 429-error if the first argument is not present.
More specifically, here's the exerpt from 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.
Based on this description, I am assuming I get the referenced error because the specified object isn't found (but I am looking at its window, so I know it's there). I have no idea why this happens, but:
Workaround
Changing this:
Set gl = Nothing
Set gl = GetObject("", "Glink.Auto")
to this:
Set gl = Nothing
Set gl = GlinkObjectHook
and adding this func:
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