为什么 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
之前。
考虑到这一点,如果 classe1
在 classe2
之前创建,它会使您的 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
通常应该避免,事实上,因为上面描述的不直观的行为)。
我制作了一个从 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
之前。
考虑到这一点,如果 classe1
在 classe2
之前创建,它会使您的 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 将在进行成员调用之前 创建 一个新实例- 这就是我之前提到的开销,并且与
答案可能在您的 C++ 代码中,而不是在客户端 VBA 代码中。 有些东西 你清理东西的方式有问题,有松散的地方某处 - 使用As New
解决一个草率的 COM 服务器我觉得这不是个好主意(As New
通常应该避免,事实上,因为上面描述的不直观的行为)。