function abc(A: Integer): Integer;
這是一個Delphi的函數聲明,看上去很簡單,只有一個參數而已,但是真實情況呢?在編譯成二進制代碼後,實際上函數的參數已經有3個了!
為了更詳細的說明問題,先用Delphi寫一個DLL,導出一個接口,接口有一個Show方法。
library Project1; uses Windows; {$R *.res} type ITest = interface procedure Show(); stdcall; end; TTest = class(TInterfacedObject, ITest) public procedure Show(); stdcall; end; function GetTest: ITest; stdcall; begin Result := TTest.Create; end; exports GetTest; { TTest } procedure TTest.Show; begin OutputDebugString('Hello World'); end; begin end.
調用方用C++編寫
#include "stdafx.h" #include#include interface ITest : public IUnknown { virtual void __stdcall show() = 0; }; typedef ITest* (WINAPI *GetITest)(); int _tmain(int argc, _TCHAR* argv[]) { HMODULE h = LoadLibrary(TEXT("Project1.dll")); if (h != 0) { GetITest get = (GetITest)GetProcAddress(h, "GetTest"); ITest *test = get(); test->show(); test->Release(); } system("pause"); return 0; }
運行後直接彈出一個內存錯誤
出錯語句在DLL中
function GetTest: ITest; stdcall; begin Result := TTest.Create; end;
以反匯編代碼的形式查看這個函數就能發現問題
可以看到,函數返回值實際上是一個隱式的參數,是一個二級指針。在Dephi中使用不會發現問題,因為它自動作出了優化。
而在混合編程中,這樣直接返回一個接口或對象的時候就會出現內存為空的錯誤。
修改後的調用代碼
#include "stdafx.h" #include#include interface ITest : public IUnknown { virtual void __stdcall show() = 0; }; // 正確的函數原型 typedef VOID (WINAPI *GetITest)(ITest**); int _tmain(int argc, _TCHAR* argv[]) { HMODULE h = LoadLibrary(TEXT("Project1.dll")); if (h != 0) { GetITest get = (GetITest)GetProcAddress(h, "GetTest"); // 修改後的調用方法 ITest *test = nullptr; get(&test); test->show(); test->Release(); } system("pause"); return 0; }