如何将对象方法作为回调方法传递给 Delphi 中的 c++ dll
How to pass an object method as a callback method to a c++ dll in Delphi
我可以将以下内容传递给旧版本的 dll
type
PCallbackList = ^TCallbackList;
TCallbackList = record
arg: Pointer;
CallBack1: procedure(arg: Pointer; p1: Pointer; error: PAnsiChar); cdecl;
CallBack2: procedure(arg: Pointer; error: PAnsiChar); cdecl;
end;
当触发回调时,我将 arg 转换为我在传递回调列表之前分配给它的对象实例。
现在 dll 已更改,新签名为:
type
PCallbackList = ^TCallbackList;
TCallbackList = record
CallBack1: procedure(p1: Pointer; error: PAnsiChar); cdecl;
CallBack2: procedure(error: PAnsiChar); cdecl;
end;
所以arg指针现在被移除了,我没办法把对象实例的引用传递给带列表的dll,所以我没办法知道回调属于哪个实例。
那么如何将对象方法作为回调方法传递?
如果新版本的DLL没有提供对旧arg
参数的任何替换,那么你基本上只有2个选择:
为每个对象实例使用一组不同的回调函数。例如,如果您有 5 个对象,则定义 5 组独立的回调函数,每个对象一组。将对象指针存储在回调可以到达它们的全局变量中。或者,为了减少全局变量的使用,如果 DLL 是线程安全的,那么您可以为每个对象创建一个单独的线程,将每个对象指针存储在 threadvar
变量中,并在适当的线程中调用回调根据需要。
为每个回调使用一个 thunk,其中目标对象 method/pointer 存储在每个 thunk 本身中。 thunk 是包含可执行指令和元数据的内存块。您可以使用 PAGE_EXECUTE(_READWRITE)
标志使用 Win32 VirtualAlloc()
函数分配这样一个块,然后将对象指针和一些专门的 x86/x64 指令放入其中,然后将其用作回调。当块被 DLL 像函数一样调用时,它的指令将被执行,然后可以根据需要对存储的指针进行操作。
在幕后,C# delegate
的行为很像 thunk,只是背后有本机编译器支持。
对于 Delphi,如果您想采用 thunk 方法,则必须手动实施 thunk,或寻找第 3 方实施。这是一个相当高级的话题。我相信在 Whosebug 上有一些关于这个主题的问题。我还为 C++Builder Journal 写了一系列关于这个主题的文章,探索 VCL 自己的 MakeObjectInstance()
thunk 的内部工作原理,它被 TWinControl
和 AllocateHWnd()
内部使用。逻辑(不是代码)可以应用于您的情况。在 my website 上,“文章”部分是该系列的前 3 篇文章 1。
1: 不幸的是,我没有完成最后的第 4 篇文章,因为系统崩溃抹去了我为它编写的所有代码。该期刊不再出版。
我可以将以下内容传递给旧版本的 dll
type
PCallbackList = ^TCallbackList;
TCallbackList = record
arg: Pointer;
CallBack1: procedure(arg: Pointer; p1: Pointer; error: PAnsiChar); cdecl;
CallBack2: procedure(arg: Pointer; error: PAnsiChar); cdecl;
end;
当触发回调时,我将 arg 转换为我在传递回调列表之前分配给它的对象实例。
现在 dll 已更改,新签名为:
type
PCallbackList = ^TCallbackList;
TCallbackList = record
CallBack1: procedure(p1: Pointer; error: PAnsiChar); cdecl;
CallBack2: procedure(error: PAnsiChar); cdecl;
end;
所以arg指针现在被移除了,我没办法把对象实例的引用传递给带列表的dll,所以我没办法知道回调属于哪个实例。
那么如何将对象方法作为回调方法传递?
如果新版本的DLL没有提供对旧arg
参数的任何替换,那么你基本上只有2个选择:
为每个对象实例使用一组不同的回调函数。例如,如果您有 5 个对象,则定义 5 组独立的回调函数,每个对象一组。将对象指针存储在回调可以到达它们的全局变量中。或者,为了减少全局变量的使用,如果 DLL 是线程安全的,那么您可以为每个对象创建一个单独的线程,将每个对象指针存储在
threadvar
变量中,并在适当的线程中调用回调根据需要。为每个回调使用一个 thunk,其中目标对象 method/pointer 存储在每个 thunk 本身中。 thunk 是包含可执行指令和元数据的内存块。您可以使用
PAGE_EXECUTE(_READWRITE)
标志使用 Win32VirtualAlloc()
函数分配这样一个块,然后将对象指针和一些专门的 x86/x64 指令放入其中,然后将其用作回调。当块被 DLL 像函数一样调用时,它的指令将被执行,然后可以根据需要对存储的指针进行操作。
在幕后,C# delegate
的行为很像 thunk,只是背后有本机编译器支持。
对于 Delphi,如果您想采用 thunk 方法,则必须手动实施 thunk,或寻找第 3 方实施。这是一个相当高级的话题。我相信在 Whosebug 上有一些关于这个主题的问题。我还为 C++Builder Journal 写了一系列关于这个主题的文章,探索 VCL 自己的 MakeObjectInstance()
thunk 的内部工作原理,它被 TWinControl
和 AllocateHWnd()
内部使用。逻辑(不是代码)可以应用于您的情况。在 my website 上,“文章”部分是该系列的前 3 篇文章 1。
1: 不幸的是,我没有完成最后的第 4 篇文章,因为系统崩溃抹去了我为它编写的所有代码。该期刊不再出版。