如何将对象方法作为回调方法传递给c++;Delphi中的动态链接库

如何将对象方法作为回调方法传递给c++;Delphi中的动态链接库,delphi,firemonkey,delphi-10.3-rio,Delphi,Firemonkey,Delphi 10.3 Rio,我可以将以下内容传递给旧版本的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; 当启动回调时,我将

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

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

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

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

    对于Delphi,如果您想使用thunk方法,则必须手动实现thunk,或者找到第三方实现。这是一个相当高级的话题。我相信在这个问题上会有一些问题。我还为C++Builder杂志写了一系列关于这个主题的文章,探讨了VCL自己的
    MakeObjectInstance()
    thunk的内部工作机制,该工具由
    TWinControl
    AllocateWnd()内部使用。逻辑(而不是代码)可以应用于您的情况。上,在“文章”部分,是该系列1的前3篇文章


    1:不幸的是,我没有完成最后的第4篇文章,因为系统崩溃,我为它编写的所有代码都被删除了。并且日志不再发布。

    如果无法传递对原始实例的引用,则无法确定它引用的是哪个实例,除非只有一个实例。在这种情况下,您需要有一个“全局”引用,即中类的全局变量或类变量question@DaveNottage不幸的是,有多个实例正在使用此dll。所以我需要一些方法来知道回调是针对哪一个的。如果每个实例都在一个单独的线程中,那么使用ThreadVar@RemyLebeau你能把你所有的评论都写下来作为回答,这样我就可以接受了吗?您基本上给出了大部分可能的解决方案。@RemyLebeau感谢您分享您宝贵的经验。