为什么 Dim as New 和 Dim/Set in VBA 在我调用 COM 服务器时表现不同?

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

我制作了一个从 VBA 调用的进程外 COM 服务器 (C++)。

出于未知原因,当我多次调用它时(在同一个子中至少两次)我只能使用 Dim xx As New xxx.

调用它

当我尝试使用 Dim xxx As xxx 然后 Set xx = new xxx 调用它时,我的 com 服务器引发违规读取异常和 VBA returns 错误代码 800706BE.

以下代码确实有效(伪代码 - 我删除了不相关的部分)。请注意,'Main' sub 调用 'aux' 函数,并且 Sub 和 'aux' 函数都调用我的 COM 服务器(两个不同的 类)。

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

以下工作:

 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

有人能解释一下为什么我的代码只适用于第一种情况吗?

另请注意,如果我只在 sub 中调用一次服务器(不调用 aux),那么两种方法(Dim as New 和 Dim/Set)都有效。


编辑

我注意到情况 1(有效的情况):我的服务器连续自动启动和停止两次(在 Windows 任务管理器中看到)。

而在第二种情况(有问题的情况下):我的服务器只启动了一次 - 没有停止并引发错误。

现在我刚刚按以下方式修改了第二种情况,异常消失了:

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

唯一的区别是我在调用它之前设置了我的服务器(而不是在调用我的“aux”函数之前对其进行初始化)。 对某人有意义吗

问题可能出在调用顺序上。根据我的经验,使用 As New 声明的对象仅在第一次成员调用时实例化,而 Set ... = New 立即实例化对象。

这么说,在第一种情况下 classe2 是在 classe1 之前创建的,只有当您调用 com1.Method1.

时才会创建

在第二种情况下,classe1 是在 Set 中创建的,在 classe2 之前。

考虑到这一点,如果 classe1classe2 之前创建,它会使您的 COM 代码以某种方式产生内存冲突。

Dim 语句不可执行。 Set 语句是。

当您执行 Dim foo As New Bar 时,您正在创建一个 自动实例化的对象变量 ,这会在 VBA 运行时产生一些开销(每个对其调用验证是否存在有效的对象引用。

这是自动实例化对象的咬合方式:

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

所以 As New 使 VBA 竭尽全力确保该指针始终有一个有效的对象引用,无论如何。对声明为 As New 的对象变量的每个成员调用都是有效的:如果引用指向 Nothing,VBA 将在进行成员调用之前 创建 一个新实例- 这就是我之前提到的开销,并且与 相矛盾。自动实例化的对象变量不是 "only instantiated on first member call" - 它们会在需要 .

时被实例化

答案可能在您的 C++ 代码中,而不是在客户端 VBA 代码中。 有些东西 你清理东西的方式有问题,有松散的地方某处 - 使用As New 解决一个草率的 COM 服务器我觉得这不是个好主意(As New 通常应该避免,事实上,因为上面描述的不直观的行为)。