当前位置 : 主页 > 编程语言 > delphi >

Delphi和C类VMT是否兼容?

来源:互联网 收集:自由互联 发布时间:2021-06-23
我需要从Delphi调用一些C代码. C代码需要能够回调到Delphi代码中.此处显示的示例 Calling a callback function in Delphi from a C++ DLL非常有效.但是,我想传递一个实现接口的Delphi对象,而不是将单个
我需要从Delphi调用一些C代码. C代码需要能够回调到Delphi代码中.此处显示的示例 Calling a callback function in Delphi from a C++ DLL非常有效.但是,我想传递一个实现接口的Delphi对象,而不是将单个Delphi函数传递给C作为回调.

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

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

ICallable接口又将在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;

在C端,编译为DLL,我想定义ICallable接口,如下所示:

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接口,而不是依赖于此类实现细节.但是,如您所知,这些是COM接口,因此您必须实现IUnknown.你说你想避免COM的机制,但你唯一需要添加的是IUnknown.在我看来,这并不是特别繁重.我觉得你认为你需要注册CLSID,实现类工厂等等.你没有.你只需要实现IUnknown.

无论如何,如果你真的开始避免使用IUnknown那么你就不能使用Delphi接口,并且我可以告诉它有两个选项:

>在Delphi代码中手动实现VMT. VMT只是一个函数指针数组.这将导致您看起来像COM在C中所做的方式的代码.完全可能,但不是很愉快.
>使用问题中概述的方法,并依赖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
网友评论