typedef void(*Fun)();
Fun P = (Fun)GetProcAddress(hDll, "print"); // 失敗,返回 NULL
Fun P = (Fun)GetProcAddress(hDll, MAKEINTRESOURCE(1)); // 成功,按序數獲取
字符集為多字節 (MBCS)。
C++ 編譯器為支持重載,會對函數名進行名稱修飾,生成類似 ?print@@YAXXZ 的導出符號。導出表中存儲的是修飾後的名稱,而非源碼中的 print。
使用 VS 自帶工具查看真實導出符號:
dumpbin /exports YourDll.dll
輸出示例:
ordinal hint RVA name
1 0 00001000 ?print@@YAXXZ = @ILT+0(?print@@YAXXZ)
2 1 00001010 ?print2@@YAXXZ = @ILT+10(?print2@@YAXXZ)
可見導出名實為 ?print@@YAXXZ,故 GetProcAddress("print") 找不到。
// Dll.h
#ifdef MYDLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
extern "C" DLL_API void print(); // 導出名為 "print"
適用於:C/C++ 混合調用、插件系統、跨語言調用。
創建 MyDll.def:
LIBRARY "MyDll"
EXPORTS
print @1
print2 @2
項目屬性 → Linker → Input → Module Definition File 填入 MyDll.def。
優點:不改動源碼,可精確控制導出名與序數;缺點:需維護額外文件。
GetProcAddress(hDll, "?print@@YAXXZ"); // 依賴編譯器版本、調用約定,極不穩定
MAKEINTRESOURCE(1) 實際傳遞低位為序數的指針,系統直接按導出表索引查找,繞過名稱比對。這也是為何序數調用總能成功的原因。
GetProcAddress 找不到函數名,99% 是因為 C++ 名稱修飾。使用 dumpbin /exports 確認真實導出名,採用 extern "C" 或 .def 文件導出純函數名,是工業級解決方案。序數調用僅作備選,不宜作為主流方式。