DLL 實現代碼複用、模組化部署與熱更新。本文以 Visual Studio 為例,完整示範非 MFC DLL 的建立、.def 匯出檔用法、隱式連結(靜態載入)與顯式連結(動態載入)兩種呼叫模式的專案設定、程式碼與優缺點對比。
DllDemo。DllDemo.h 定義匯出巨集:#ifdef DllDemo_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
extern "C" {
DLL_API int __stdcall Max(int a, int b); // __stdcall 便於跨語言呼叫
}
DllDemo.cpp 實作:#include "DllDemo.h"
DLL_API int __stdcall Max(int a, int b) {
return (a > b) ? a : (a < b) ? b : 0; // 相等回傳 0
}
DllDemo.dll、DllDemo.lib、DllDemo.h。__declspec); DllDemo.def
LIBRARY MyDll
EXPORTS
Max @1
專案屬性 → 連結器 → 輸入 → 模組定義檔 指向 DllDemo.def,重新編譯即可。
DllDemo.h、DllDemo.lib、DllDemo.dll。#include "DllDemo.h"
#pragma comment(lib, "DllDemo.lib") // 告訴連結器連結 .lib
int main() {
int v = Max(2, 9); // 直接呼叫,像普通函式
printf("Max = %d\n", v);
return 0;
}
DllDemo.dll,不需 .lib/.h。typedef int (__stdcall *LPMAX)(int, int); // 函式指標型別
HINSTANCE hDll = LoadLibrary(TEXT("DllDemo.dll"));
if (!hDll) return -1;
LPMAX pMax = (LPMAX)GetProcAddress(hDll, "Max"); // 或序數 MAKEINTRESOURCE(1)
if (pMax) {
int v = pMax(2, 9);
printf("Max = %d\n", v);
}
FreeLibrary(hDll);
| 問題 | 原因 | 解決 |
|---|---|---|
| LNK2019 無法解析外部符號 | 未連結 .lib 或 .def 匯出名稱不符 | 檢查 dumpbin /exports DllDemo.dll 實際匯出名稱 |
| 呼叫慣例不符導致堆疊破壞 | DLL 用 __stdcall,呼叫端預設 __cdecl | 雙方統一宣告 __stdcall 或 WINAPI |
| C++ 名稱修飾導致 GetProcAddress 失敗 | 未加 extern "C" | DLL 標頭與實作皆包裹 extern "C" |
| Debug/Release CRT 不匹配 | DLL 與 EXE 連結不同版本 CRT | 統一專案屬性 → C/C++ → 代碼產生 → 執行階段程式庫 |
「隱式連結適合穩定內部介面,顯式連結適合插件/熱插拔;兩者常共存於同一專案不同模組。」