【问题标题】:Why Dim as New and Dim/Set in VBA behave differently when I call a COM server?为什么当我调用 COM 服务器时,VBA 中的 Dim as New 和 Dim/Set 的行为不同?
【发布时间】:2018-02-03 13:19:45
【问题描述】:

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

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

当我尝试使用 Dim xxx As xxx 然后 Set xx = new xxx 调用它时,我的 com 服务器会引发冲突读取异常,并且 VBA 返回错误代码 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

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

【问题讨论】:

    标签: vba com


    【解决方案1】:

    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 将在进行成员调用之前创建一个新实例 - 这是我前面提到的开销,与 @ 相矛盾987654321@。自动实例化的对象变量不是“仅在第一次成员调用时实例化”——它们会在需要时被实例化

    答案可能在您的 C++ 代码中,而不是在客户端 VBA 代码中。 某事你清理东西的方式有问题,有松散的结局某处 - 使用As New 来解决一个草率的 COM 服务器并没有让我印象深刻想法(事实上,As New 应该避免,因为上面描述的不直观的行为)。

    【讨论】:

    • 谢谢,请看我的编辑,在我重新安排我的服务器初始化后异常消失了,但我不明白为什么。
    • 如果没有 C++ 代码,我认为您的问题无法完全回答。两个初始化对应两个As New声明;为什么它停止不是 VBA 的错,而可能是 COM 服务器中的错误。
    • 你是对的,问题来自我的 c++ 代码,我在我的 ATL 类中而不是在主 CAtlExeModuleT 模块中初始化了一些第三方运行时。现在这两种情况都适用,感谢您指出正确的方向和您的解释。
    【解决方案2】:

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

    这样说,在第一种情况下,classe2 是在 classe1 之前创建的,而 classe1 仅在您调用 com1.Method1 时创建。

    在第二种情况下,classe1Set 中创建,在classe2 之前。

    考虑到这一点,如果在classe2 之前创建classe1,它会以某种方式连接您的COM 代码创建内存冲突。

    【讨论】:

    • 感谢这个想法,我正在调查它,但在此之前这两个类应该是独立的。
    猜你喜欢
    • 2011-04-21
    • 2023-02-14
    • 2010-10-06
    • 2015-11-02
    • 2022-11-24
    • 2014-08-14
    • 1970-01-01
    相关资源
    最近更新 更多