我对你的问题的评论只是部分正确。我仍然相信Me
关键字在防止类方法“重定向”到标准 .bas 模块中的方法方面发挥作用。但这仅适用于早期绑定。
IDispatch::Invoke 实际上可以毫无问题地调用 .bas 模块中的方法。您的初始方法签名是正确的:
Public Function Meow(ByRef meObj As Class1) As Long
Class1
代码:
Option Explicit
Public Sub Meow()
Debug.Print "Meow"
End Sub
Public Sub Woof()
Debug.Print "Woof"
End Sub
标准 .bas 模块中的代码:
Option Explicit
Sub Test()
Dim c As Object 'Must be late-binded!
Dim vTblPtr As LongPtr
Dim vTblMeowPtr As LongPtr
Dim originalMeow As LongPtr
'
Set c = New Class1
c.Meow 'Prints "Meow" to the Immediate Window
'
'The address of the virtual table
vTblPtr = MemLongPtr(ObjPtr(c))
'
'The address of the Class1.Meow method within the virtual table
vTblMeowPtr = vTblPtr + 7 * PTR_SIZE
'
'The current address of the Class1.Meow method
originalMeow = MemLongPtr(vTblMeowPtr)
'
'Replace the address of Meow with the one in a .bas module
MemLongPtr(vTblMeowPtr) = VBA.Int(AddressOf Moew)
'
c.Meow 'Prints "Meow in .bas" to the Immediate Window
'
'Revert the original address
MemLongPtr(vTblMeowPtr) = originalMeow
'
c.Meow 'Prints "Meow" to the Immediate Window
End Sub
Public Function Moew(ByVal this As Class1) As Long
Debug.Print "Meow in .bas"
End Function
我使用LibMemory进行内存操作。
如果您将Meow
类方法更改为 aFunction
而不是 a,Sub
那么您需要在 .bas 模块中ByRef
的方法内的参数列表末尾有一个额外的参数。Meow
编辑#1
我想到了下面评论中讨论的问题,我能想到的唯一原因是 IDispatch 仅适用于指向 IUnknown 接口的指针。
这意味着:
Public Function Meow(ByRef this As Class1) As Long
将使应用程序崩溃
但是,这有效:
Public Function Moew(ByVal this As Class1) As Long
Debug.Print "Meow in .bas"
End Function
因为传递ByVal
会强制 IUnknown 上的 QueryInterface 和 AddRef (退出范围时使用 Release)
这也有效:
Public Function Moew(ByRef this As IUnknown) As Long
Debug.Print "Meow in .bas"
End Function
编辑#2
为再次编辑表示歉意。
Invoke 方法不适用于指向 IUnknown 的指针。它正在使用指向 IDispatch 的指针。这可以通过以下方式检查:
Public Function Moew(ByVal this As LongPtr) As Long
Debug.Print this
Debug.Print "Meow in .bas"
End Function
这会将 ptr 打印到 IDispatch 接口。那么,为什么会ByRef this As Class1
失败呢?为什么做ByVal this As Class1
和ByRef this As IUnknown
工作?
ByRef this As Class1
我相信 VB 无法访问 VarPtr(this) 地址,因此我们正在读取我们不应该读取的内存。IUnknown 接口上没有额外的 AddRef 或 Release,因为该方法永远不会使用此声明调用。当 Invoke 试图调用该方法时,应用程序就会崩溃。
ByVal this As Class1
该方法只是创建一个 VB 变量(在 VB 内存空间上)并调用 AddRef
ByRef this As IUnknown
由于这不是双接口,因此完成了对 QueryInterface 和 AddRef 的调用。'this' 的内存地址在本地内存空间上,与第二个示例相同。