I have made an out-of-process COM server (C++) that is called from VBA.
For an unknown reason when I call it multiple times (at least two times in the same sub) I'm only able to call it using Dim xx As New xxx
.
When I try to call it using Dim xxx As xxx
and then Set xx = new xxx
, my com server raises a violation reading exception and VBA returns the error code 800706BE
.
The following code does work (pseudo code - I removed the irrelevant part). Note that the 'Main' sub call the 'aux' function and that both the Sub and the 'aux' function call my COM server (two different classes).
Function aux() As Double()
Dim com As New COMServer.classe2
Dim Returns() As Double
Returns = com.Method2 'actual call to the COM Server
aux = Returns
End Function
Sub Main()
Dim Resultat() As Double
Dim com1 As New COMServer.classe1
Dim Returns() As Double
Returns = aux ' call Function aux
Resultat = com1.Method1(Returns) 'actual call to the COM Server
End Sub
The following does not work :
Function aux() As Double()
Dim com As COMServer.classe2
Set com = New COMServer.classe2
Dim Returns() As Double
Returns = com.Method2 'actual call to the COM Server
aux = Returns
End Function
Sub Main()
Dim Resultat() As Double
Dim com1 As COMServer.classe1
Set com1 = New COMServer.classe1
Dim Returns() As Double
Returns = aux ' call Function aux
Resultat = com1.Method1(Returns) 'a violation reading (c++) Exception is thrown here
End Sub
Can someone explain me why my code only works in the first case ?
Also note that if I only call the server once in the sub (no call to aux), then both methods ( Dim as New and Dim/Set ) work.
EDIT
I have noticed that in case 1 (the case that works) : my server automatically start and stop two times consecutively (seen in the Windows Task Manager ).
Whereas in second case (the buggy one) : my server start only once - didn't stop and raise the error.
Now I have just modified the second case in the following manner and the exception disappears :
Sub Main()
Dim Resultat() As Double
Dim Returns() As Double
Returns = aux ' call Function aux
Dim com1 As COMServer.classe1
Set com1 = New COMServer.classe1
Resultat = com1.Method1(Returns) 'no more Exception
End Sub
The only difference is that I set my server just before to call it (instead to initialize it before to call my 'aux" function). Does it makes sense to someone ?
Dim
statements aren't executable. Set
statements are.
When you do Dim foo As New Bar
you're creating an auto-instantiated object variable, which incurs a bit of overhead in the VBA runtime (every call against it validates whether there's a valid object reference).
This is how auto-instantiated objects bite:
Dim foo As New Collection
Set foo = Nothing
foo.Add 42 'runtime error 91? nope.
Debug.Print foo.Count ' prints 1
Set foo = Nothing
Debug.Print foo.Count ' runtime error 91? nope. prints 0
So As New
makes VBA go out of its way to ensure there's always a valid object reference for that pointer, no matter what. Every member call on object variables declared As New
is valid: VBA will create a new instance before making the member call if the reference points to Nothing
- that's the overhead I mentioned earlier, and contradicts LS_dev's answer. Auto-instantiated object variables aren't "only instantiated on first member call" - they're instantiated whenever they need to be.
The answer is likely in your C++ code, not in the client VBA code. Something is wrong with how you're cleaning things up, there's loose ends somewhere - using As New
to work around a sloppy COM server doesn't strike me as a good idea (As New
should generally be avoided, as a matter of fact, for the unintuitive behavior depicted above).