c++ - Delphi 和 C++ 类 VMT 兼容吗?

标签 c++ delphi delphi-7

我需要从 Delphi 调用一些 C++ 代码。作为返回,C++ 代码需要能够回调到 Delphi 代码中。此处显示的示例 Calling a callback function in Delphi from a C++ DLL工作得很好。但是,我不想将单个 Delphi 函数作为回调传递给 C++,而是传递一个实现接口(interface)的 Delphi 对象。

编辑:通过接口(interface),我指的是 C++ 术语,它是一个具有纯虚函数的类。这不一定是用 Delphi interface 关键字定义的类型。换句话说,下面的类定义了一个我想从 C++ 调用的接口(interface):

ICallable = class 
    procedure callMe stdcall; virtual; abstract;
    procedure CallMeAgain stdcall; virtual; abstract;
end;

ICallable 接口(interface)将依次在 Delphi 中实现如下:

MyCallable = class(ICallable)
   procedure callMe override;
   procedure callMeAgain override;
end;

procedure MyCallable.callMe
begin
   WriteLine('I was called');
end;

procedure MyCallable.callMeAgain
begin
   WriteLine('I was called again');
end;

在编译为 DLL 的 C++ 端,我想按如下方式定义 ICallable 接口(interface):

class ICallable{
public:
  virtual void callMe()=0;
  virtual void callMeAgain()=0;
}

并导出如下DLL函数,以便Delphi调用:

#define DllExport   extern "C" __declspec( dllexport )

DLLExport bool Callback(ICallable* callable){
   callable->callMe();
   callable->callMeAgain();
   return true;
}  

最后回到 Delphi:

function Callback(myCallable: ICallable) : Boolean cdecl; external 'dllname'

问题:

  • 只有 如果 C++ 和 Delphi 以相同的方式实现它们的虚拟方法表,这才有效。是这样吗?

最佳答案

This can only work of C++ and Delphi implement their virtual method tables in the same way. Is it the case?

我最初认为Delphi 类没有与C++ 类兼容的VMT。我认为这是因为所有 Delphi 类都派生自声明了虚方法的 TObject。这些虚拟方法出现在 VMT 中 我假设这些方法将首先出现在 VMT 中。然而,编译器安排 TObject 的内置虚拟方法在 VMT 中具有负索引。这意味着用户定义的虚拟方法(那些在 TObject 的子类中定义的)从索引 0 开始。

这意味着问题代码中的 Delphi 和 C++ 类确实具有兼容的 VMT。我相信这种设计选择是为了在早期版本的 Delphi 中支持 COM。为了支持我的说法,我建议你引用 documentation我强调说:

The layout of a VMT is shown in the following table. On the 32-bit platforms, at positive offsets, a VMT consists of a list of 32-bit method pointers (64-bit method pointers on the 64-bit platform)--one per user-defined virtual method in the class type--in order of declaration. Each slot contains the address of the corresponding entry point of the virtual method. This layout is compatible with a C++ v-table and with COM. At negative offsets, a VMT contains a number of fields that are internal to Delphi's implementation. Applications should use the methods defined in TObject to query this information, since the layout is likely to change in future implementations of the Delphi language.

应该强调的是,C++ 标准中没有任何内容强制要求对虚拟方法使用 VMT,更不用说 VMT 的实现方式了。实际上,每个主流 Windows 编译器都以这种方式实现了 VMT,以支持 COM。

您可以使用 Delphi 接口(interface),而不是依赖于此类实现细节。但是,如您所知,这些是 COM 接口(interface),因此您必须实现 IUnknown。你说你想避开 COM 机制,但你唯一需要添加的是 IUnknown。在我看来,这并不是特别繁重。我觉得您认为您需要注册 CLSID、实现类工厂等等。你不知道。您只需要实现 IUnknown

无论如何,如果您真的打算避免使用 IUnknown,那么您就不能使用 Delphi 接口(interface),据我所知有两个选择:

  1. 在您的 Delphi 代码中手动实现 VMT。 VMT 只是一个函数指针数组。这将引导您编写看起来像 COM 在 C 中的方式的代码。完全可能,但并不完全令人愉快。
  2. 使用您问题中概述的方法,并依赖于 TObject 对其内置虚拟方法使用负 VMT 索引的实现细节。

可编译代码选项 2 如下所示:

德尔福

{$APPTYPE CONSOLE}

type
  ICallable = class
  public
    procedure CallMe cdecl; virtual; abstract;
    procedure CallMeAgain cdecl; virtual; abstract;
  end;

  MyCallable = class(ICallable)
  public
    procedure CallMe; override;
    procedure CallMeAgain; override;
  end;

procedure MyCallable.CallMe;
begin
  Writeln('CallMe');
end;

procedure MyCallable.CallMeAgain;
begin
  Writeln('CallMeAgain');
end;

const
  dllname = 'C:\Users\heff\Desktop\Win32Project1\Debug\Win32Project1.dll';

function Callback(Callable: ICallable): Boolean; cdecl; external dllname;

var
  Callable: ICallable;

begin
  Callable := MyCallable.Create;
  Writeln(Callback(Callable));
  Callable.Free;
  Readln;
end.

C++

#include <Windows.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

class ICallable
{
public:
    virtual void CallMe() = 0;
    virtual void CallMeAgain() = 0;
};

extern "C" __declspec(dllexport) bool Callback(ICallable* callable)
{
    callable->CallMe();
    callable->CallMeAgain();
    return true;
}

输出

CallMe
CallMeAgain
TRUE

关于c++ - Delphi 和 C++ 类 VMT 兼容吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36167559/

相关文章:

c++ - 初始化后如何从字符串中分配位集值

c++ - 尝试理解简单的大数计算

ios - 如何正确地通过 IPv6 发送电子邮件?

Delphi FormKeyDown ALT 键卡住

delphi - 在另一个窗体的 OnDestroy 事件中释放窗体时发生访问冲突

c++ - 在 C 中使用共享库很好,但在 C++ 中使用相同的代码不好吗?

c++ - 类型结果之间的意外差异

delphi - 检查某个值是否在集合中的替代方法

delphi - Delphi 7 中的类变量

delphi - 在Delphi中使用SSE进行舍入