首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

GetProcAddress 無法按函數名獲取地址:DLL 導出符號修飾與 .def 文件解決方案

     編輯:程式設計疑難解答

現象

typedef void(*Fun)();
Fun P = (Fun)GetProcAddress(hDll, "print");     // 失敗,返回 NULL
Fun P = (Fun)GetProcAddress(hDll, MAKEINTRESOURCE(1)); // 成功,按序數獲取

字符集為多字節 (MBCS)。

根本原因:C++ 名稱修飾

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") 找不到。

三種解決方案

1. 使用 extern "C" 禁用修飾(推薦 C 接口)

// 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++ 混合調用、插件系統、跨語言調用。

2. 使用 .def 文件指定導出名

創建 MyDll.def:

LIBRARY "MyDll"
EXPORTS
    print   @1
    print2  @2

項目屬性 → Linker → Input → Module Definition File 填入 MyDll.def。
優點:不改動源碼,可精確控制導出名與序數;缺點:需維護額外文件。

3. 直接使用修飾名(極不推薦)

GetProcAddress(hDll, "?print@@YAXXZ"); // 依賴編譯器版本、調用約定,極不穩定

序數調用原理

MAKEINTRESOURCE(1) 實際傳遞低位為序數的指針,系統直接按導出表索引查找,繞過名稱比對。這也是為何序數調用總能成功的原因。

最佳實踐建議

  1. 對外接口統一用 extern "C" + __stdcall,確保穩定 ABI。
  2. 避免導出 C++ 類/重載函數,若必須導出類,需確保調用方使用相同編譯器版本及 CRT。
  3. 提供頭文件與 .lib,讓調用方隱式鏈接,完全避免 GetProcAddress。

總結

GetProcAddress 找不到函數名,99% 是因為 C++ 名稱修飾。使用 dumpbin /exports 確認真實導出名,採用 extern "C" 或 .def 文件導出純函數名,是工業級解決方案。序數調用僅作備選,不宜作為主流方式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved