Delphi和C类VMT是否兼容?
我需要从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以相同方式实现其虚方法表时才有效.是这样的吗? 解决方法
我原本以为Delphi类没有与C类兼容的VMT.我认为这是因为所有Delphi类都派生自TObject,它声明了虚方法.这些虚拟方法出现在VMT中我假设这些方法首先出现在VMT中.然而,发现编译器安排TObject的内置虚拟方法在VMT中具有负索引.这意味着用户定义的虚拟方法(在TObject的子类中定义的那些)从索引0开始. 这意味着问题代码中的Delphi和C类确实具有兼容的VMT.我相信这个设计选择是为了支持早期版本的Delphi中的COM.为了支持我的主张,我建议你参加documentation,并强调:
应该强调的是,C标准中没有任何内容要求将VMT用于虚拟方法,更不用说VMT的实现方式.实际上,每个主流的Windows编译器都有这样实现的VMT,以支持COM. 您可以使用Delphi接口,而不是依赖于此类实现细节.但是,如您所知,这些是COM接口,因此您必须实现IUnknown.你说你想避免COM的机制,但你唯一需要添加的是IUnknown.在我看来,这并不是特别繁重.我觉得你认为你需要注册CLSID,实现类工厂等等.你没有.你只需要实现IUnknown. 无论如何,如果你真的开始避免使用IUnknown那么你就不能使用Delphi接口,并且我可以告诉它有两个选项: >在Delphi代码中手动实现VMT. VMT只是一个函数指针数组.这将导致您看起来像COM在C中所做的方式的代码.完全可能,但不是很愉快. 可编译代码选项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:UsersheffDesktopWin32Project1DebugWin32Project1.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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |