專案引用一個現成動態庫 (.lib + .dll),頭文件導出宏定義如下:
#ifdef NetLink
#define NETDIR __declspec(dllexport)
#else
#define NETDIR __declspec(dllimport)
#endif
NETDIR void LinkDir(); // 導出函數聲明
動態庫編譯、使用均正常。需求來了:**能否編譯為靜態庫 (.lib 只含目標代碼,無 DLL 依賴) 直接鏈接?**
LNK2019: 無法解析的外部符號。extern "C" __declspec(dllexport):錯誤變為 _imp_LinkDir 仍無法解析。__declspec(dllimport) 告訴編譯器:該符號來自 DLL,**產生間接調用指令** (讀取 IAT 表 __imp_FuncName),而非直接調用。__declspec(dllexport) 告訴鏈接器:將符號導出到 DLL 導出表。NETDIR 宏展開為 dllimport,引用端期望從 DLL 導入,鏈接器在靜態庫中找不到 __imp_ 符號 → LNK2019。NETDIR = extern "C"// 靜態庫專用頭文件配置
#define NETDIR extern "C" // 禁用 C++ Name Mangling,生成標準 C 符號名
NETDIR void LinkDir(); // 等價於 extern "C" void LinkDir();
效果:
_LinkDir (C 風格) 或 LinkDir (視調用約定),而非 C++ 裝飾名 ?LinkDir@@YAXXZ。_LinkDir,無 __imp_ 前綴,無 DLL 依賴。// MyLib.h
#ifdef MYLIB_EXPORTS // 定義於動態庫項目預處理器
#define MYLIB_API __declspec(dllexport)
#elif defined(MYLIB_STATIC) // 定義於靜態庫項目/引用端靜態鏈接
#define MYLIB_API extern "C"
#else // 引用動態庫
#define MYLIB_API __declspec(dllimport)
#endif
MYLIB_API void LinkDir();
| 構建類型 | 預處理器定義 | 宏展開 | 符號特徵 |
|---|---|---|---|
| 動態庫 | MYLIB_EXPORTS | __declspec(dllexport) | 導出表含符號 |
| 靜態庫 | MYLIB_STATIC | extern "C" | 普通外部符號,C 鏈接 |
| 引用動態庫 | 無 | __declspec(dllimport) | 間接調用 __imp_ |
| 引用靜態庫 | MYLIB_STATIC | extern "C" | 直接解析符號 |
靜態庫導出函數只需
extern "C"即可! 去掉所有__declspec(dllimport/export),消除 DLL 專屬的導入庫機制依賴,實現純目標代碼鏈接。
此文作為踩坑備忘,高手可忽略,初學者建議收藏對照宏定義表配置項目。