Search code examples
vbacom

Why Dim as New and Dim/Set in VBA behave differently when I call a COM server?


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 ?


Solution

  • 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).