如何从C语言DLL中获取IXeoma接口并在Delphi中调用?
在Delphi中正确调用C++ DLL接口的解决方案
问题描述
我有一个C++编写的DLL库,附带描述接口的头文件,DLL里包含一个用于获取接口的函数。已成功加载库,但调用该函数返回nil,需要解决如何在Delphi中正确定义并获取这个接口。
C++头文件内容
using DllCallbackClassPtr = void*; using DllCallbackFunction = void(*)(const char *, DllCallbackClassPtr); #ifdef _WIN32 #include <Windows.h> __interface IXeoma { public: enum ConnectErrorCode { OK = 0, SERVER_NOT_FOUND, WRONG_PASSWORD, UNKNOWN }; // return ConnectErrorCode virtual int start(const char* connectionString) = 0; virtual bool isConnected() = 0; virtual void stop() = 0; virtual void requestData(const char* request, const char* additionalData, DllCallbackClassPtr classPtr, DllCallbackFunction callbackFunc) = 0; virtual const char* getRequestResult(const char* request) = 0; virtual void setCameraRenderHandle(const char* previewId, HWND hWnd) = 0; };
原始Delphi错误代码
type IXeoma = interface function Start(connectionString: PChar): integer; end; type TCreateXeomaInterface = function() : IXeoma; stdcall; var Form1: TForm1; CreateXeomaInterface: TCreateXeomaInterface; implementation {$R *.dfm} var LibraryHandle: THandle; procedure TForm1.Button1Click(Sender: TObject); var XeomaInt: IXeoma; i: integer; begin LibraryHandle := LoadLibrary(PChar('D:\Projects\XeomaSDK\Win32\Debug\xeomaclientdll.dll')); if LibraryHandle >= 32 then begin @CreateXeomaInterface := GetProcAddress(LibraryHandle, 'createXeomaInterface'); end; XeomaInt := CreateXeomaInterface(); // Here XeomaInt = nil end;
核心问题分析与修正步骤
- 接口定义不完整:原始Delphi接口只实现了
Start方法,C++接口的虚函数表顺序必须完全匹配,缺失方法会导致接口识别失败。 - 调用约定不匹配:C虚函数默认使用
thiscall调用约定,Delphi接口方法需显式指定匹配的调用约定;导出函数的调用约定也需与C代码一致。 - 缺少GUID声明:C++的
__interface在Windows环境下会隐含生成GUID(或显式指定),Delphi接口必须声明相同的GUID才能正确识别接口指针。 - 错误检查缺失:未检查
GetProcAddress是否成功,可能存在导出函数名被C++名字修饰改变的情况。 - 回调类型未定义:C++中的回调函数类型需在Delphi中正确映射。
修正后的Delphi代码
type // 映射C++的回调类型,注意调用约定需与C++一致 PDllCallbackClassPtr = Pointer; TDllCallbackFunction = procedure(requestResult: PChar; classPtr: PDllCallbackClassPtr); stdcall; // 映射C++的枚举类型 TConnectErrorCode = ( ceOK = 0, ceServerNotFound, ceWrongPassword, ceUnknown ); // 完整定义接口,需匹配C++虚函数顺序、参数、调用约定 // 替换GUID为C++接口实际的GUID(可通过dumpbin或C++头文件的__declspec(uuid)获取) IXeoma = interface ['{00000000-0000-0000-0000-000000000000}'] function Start(connectionString: PChar): Integer; stdcall; function IsConnected: Boolean; stdcall; procedure Stop; stdcall; procedure RequestData(request: PChar; additionalData: PChar; classPtr: PDllCallbackClassPtr; callbackFunc: TDllCallbackFunction); stdcall; function GetRequestResult(request: PChar): PChar; stdcall; procedure SetCameraRenderHandle(previewId: PChar; hWnd: HWND); stdcall; end; // 定义获取接口的函数,调用约定需与C++导出函数一致 // 若C++导出用__cdecl,此处改为cdecl;若用__stdcall则保留stdcall TCreateXeomaInterface = function: IXeoma; cdecl; var Form1: TForm1; CreateXeomaInterface: TCreateXeomaInterface; LibraryHandle: THandle; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var XeomaInt: IXeoma; begin LibraryHandle := LoadLibrary('D:\Projects\XeomaSDK\Win32\Debug\xeomaclientdll.dll'); if LibraryHandle = 0 then begin ShowMessage('DLL加载失败,错误码:' + IntToStr(GetLastError)); Exit; end; // 获取导出函数地址,注意函数名是否被C++名字修饰 @CreateXeomaInterface := GetProcAddress(LibraryHandle, 'createXeomaInterface'); if not Assigned(CreateXeomaInterface) then begin // 若未找到,尝试使用C++名字修饰后的函数名(可通过dumpbin /exports 工具查看) @CreateXeomaInterface := GetProcAddress(LibraryHandle, '?createXeomaInterface@@YAPAVIXeoma@@XZ'); if not Assigned(CreateXeomaInterface) then begin ShowMessage('无法获取createXeomaInterface函数地址'); FreeLibrary(LibraryHandle); Exit; end; end; XeomaInt := CreateXeomaInterface(); if Assigned(XeomaInt) then begin ShowMessage('接口获取成功'); // 示例:调用接口方法 // var ConnResult := XeomaInt.Start('your_connection_string'); end else begin ShowMessage('接口获取失败,返回nil'); FreeLibrary(LibraryHandle); end; end;
额外注意事项
- 导出函数名检查:若C代码未使用
extern "C"修饰导出函数,函数名会被C名字修饰改变,需用dumpbin /exports xeomaclientdll.dll命令查看实际导出名,或修改C++代码添加extern "C"以使用原函数名。 - GUID一致性:若C++接口使用
__declspec(uuid("GUID字符串"))指定GUID,Delphi接口必须完全复制该GUID字符串。 - 调用约定验证:确认C++导出函数的调用约定(
__cdecl/__stdcall),Delphi中需对应声明,否则会导致栈错误或返回nil。
内容的提问来源于stack exchange,提问作者Канунников Александр
相关产品推荐
相关产品推荐

