動態連結庫 (DLL) 是 Windows 程式碼複用、模組化的核心機制。MFC 提供了三種 DLL 專案類型,並透過 AFX_MANAGE_STATE 宏解決跨模組資源存取難題。本文涵蓋 DLL 基礎知識、MFC 三種 DLL 架構差異、導出函數/類別的最佳實踐、DllMain 實現細節與模組狀態切換原理。
LoadLibrary/GetProcAddress/FreeLibrary)。| 慣例 | 參數壓棧 | 清理棧 | C 修飾名範例 | 適用場景 |
|---|---|---|---|---|
__cdecl | 右至左 | 呼叫者 | _func | C/C++ 預設,支援可變參數 |
__stdcall | 右至左 | 被呼叫者 | _func@12 | Win32 API 標準,固定參數 |
__fastcall | 前兩參數用 ECX/EDX | 被呼叫者 | @func@12 | 高效能,寄存器傳遞 |
thiscall | this 在 ECX,其餘右至左 | 被呼叫者 | C++ 成員函數專用 | 類別成員函數 |
extern "C" 可抑制 C++ 修飾,實現 C 風格導出,跨語言呼叫必備。
extern "C" __declspec(dllexport) YourFunc(),可被任何 Win32 應用呼叫。CWinApp 衍生類,但無訊息迴路(無 Run)。AFX_MANAGE_STATE(AfxGetStaticModuleState())CWinApp 物件;必須實現 DllMain。DllMain 中呼叫 AfxInitExtensionModule,檢查返回值。CRuntimeClass 或資源,必須提供初始化函數建立 CDynLinkLibrary 物件並導出該函數;宿主 APP 在 InitInstance 中呼叫之。BOOL WINAPI DllMain(HINSTANCE hInst, DWORD ul_reason, LPVOID lpReserved) {
switch (ul_reason) {
case DLL_PROCESS_ATTACH: /* 初始化 */ break;
case DLL_THREAD_ATTACH: /* 線程附加 */ break;
case DLL_THREAD_DETACH: /* 線程分離 */ break;
case DLL_PROCESS_DETACH: /* 清理 */ break;
}
return TRUE;
}
注意:TerminateProcess/Thread 不會觸發 DllMain。
連結 CRT DLL 版本(多線程)時必須指定為入口點。職責:
DllMain 與 RawDllMain(動態連結 MFC 時需靜態連結的輔助函數,處理模組狀態)。MFC 提供的 DllMain 會自動呼叫 CWinApp::InitInstance/ExitInstance;延伸 DLL 必須自行實現。
EXPORTS 區列出導出名(C++ 需用修飾名,可用 dumpbin /SYMBOLS 取得)。支援序號導出(效率最高)。__declspec(dllexport) / MFC 宏 (推薦):_AFXEXT 時展開為 dllexport,使用端為 dllimport:AFX_EXT_CLASS(類別)、AFX_EXT_API(函數)、AFX_EXT_DATA(資料)。class AFX_EXT_CLASS CMyClass : public CObject { ... };建議優先使用第 3 種(維護性最佳),次之 .def(序號導出效率高),最後命令列。
動態連結 MFC 的一般 DLL 導出函數必須以此宏開頭。它構造一個 AFX_MODULE_STATE 切換器,將當前線程的模組狀態指標暫時指向該 DLL 的靜態模組狀態(AfxGetStaticModuleState() 返回),函數返回時自動恢復。這確保 AfxGetResourceHandle()、AfxGetApp()、類別工廠映射等均指向 DLL 自身資源,而非宿主 EXE。詳細機制見第 9 章 9.4.1 節。