如何在Delphi中将对象方法作为回调方法传递给c++dll



我可以将以下内容传递到旧版本的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参数的替代品,那么您基本上只有两个选择:

  1. 为每个对象实例使用一组不同的回调函数。例如,如果您有5个对象,那么定义5个单独的回调函数集,每个对象一个。将对象指针存储在全局变量中,回调可以到达这些变量。或者,为了减少全局变量的使用,如果DLL是线程安全的,那么您可以为每个对象创建一个单独的线程,将每个对象指针存储在threadvar变量中,并根据需要在适当的线程中调用回调。

  2. 对每个回调使用一个thunk,其中目标对象方法/指针存储在每个thunk本身中。thunk是一个包含可执行指令和元数据的内存块。您可以使用PAGE_EXECUTE(_READWRITE)标志使用Win32VirtualAlloc()函数分配这样一个块,然后将对象指针和一些专用的x86/x64指令放入其中,然后将其用作回调。当DLL像函数一样调用块时,它的指令将被执行,然后可以根据需要对存储的指针进行操作。

在幕后,C#delegate的行为很像一个thunk,只是背后有本机编译器的支持。

对于Delphi,如果你想采用thunk方法,你必须手动实现thunk,或者找到第三方实现。这是一个相当高级的话题。我相信在StackOverflow上有一些关于这个主题的问题。我还为C++Builder Journal写了一系列关于这个主题的文章,探讨了TWinControlAllocateHWnd()内部使用的VCL自己的MakeObjectInstance()thunk的内部工作原理。逻辑(而不是代码(可以应用于您的情况。在我的网站上;文章";部分是该系列1的前3篇文章。

1:不幸的是,我一直没有完成最后的第4篇文章,因为系统崩溃,我为它写的所有代码都被删除了。《华尔街日报》也不再出版了

最新更新