【问题标题】:How to pass an object method as a callback method to a c++ dll in Delphi如何将对象方法作为回调方法传递给Delphi中的c ++ dll
【发布时间】:2021-03-06 16:49:12
【问题描述】:

我可以将以下内容传递给旧版本的 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,所以我无法知道回调属于哪个实例。

那么如何将对象方法作为回调方法传递呢?

【问题讨论】:

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

标签: delphi firemonkey delphi-10.3-rio


【解决方案1】:

如果新版本的DLL没有提供任何替换旧的arg参数,那么你基本上只有2个选择:

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

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

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

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

1:不幸的是,我没有完成最后的第 4 篇文章,因为系统崩溃导致我为它编写的所有代码都消失了。并且该期刊已不再出版。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多