首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC DLL 開發完全指南:三種類型、導出技巧與模組狀態管理

     編輯:關於VC++

動態連結庫 (DLL) 是 Windows 程式碼複用、模組化的核心機制。MFC 提供了三種 DLL 專案類型,並透過 AFX_MANAGE_STATE 宏解決跨模組資源存取難題。本文涵蓋 DLL 基礎知識、MFC 三種 DLL 架構差異、導出函數/類別的最佳實踐、DllMain 實現細節與模組狀態切換原理。

一、 DLL 基礎知識速覽

1. 靜態 vs 動態連結

  • 靜態連結:連結器將庫目標模組複製進 EXE,體積大但獨立。
  • 動態連結:EXE 僅含導入記錄(指向 DLL 名稱與序號/名稱),執行時由載入器映射 DLL 到進程空間。分為載入時動態連結(隱式連結,需 .lib)與執行時動態連結(顯式連結,LoadLibrary/GetProcAddress/FreeLibrary)。

2. 關鍵檔案類型

  • .lib (導入庫):COFF 格式,含導出符號的動態連結資訊,連結時使用。
  • .exp (導出檔):連結器生成 DLL 時使用的導出資訊。
  • .map (映像檔):含區段、符號、入口點、修正資訊,除錯利器。

3. 呼叫慣例與修飾名

慣例參數壓棧清理棧C 修飾名範例適用場景
__cdecl右至左呼叫者_funcC/C++ 預設,支援可變參數
__stdcall右至左被呼叫者_func@12Win32 API 標準,固定參數
__fastcall前兩參數用 ECX/EDX被呼叫者@func@12高效能,寄存器傳遞
thiscallthis 在 ECX,其餘右至左被呼叫者C++ 成員函數專用類別成員函數

extern "C" 可抑制 C++ 修飾,實現 C 風格導出,跨語言呼叫必備。

二、 MFC 三種 DLL 應用程式類型

1. 靜態連結 MFC 的一般 DLL (Regular DLL Statically Linked to MFC)

  • 導出函數:extern "C" __declspec(dllexport) YourFunc(),可被任何 Win32 應用呼叫。
  • 擁有 CWinApp 衍生類,但無訊息迴路(無 Run)。
  • MFC 程式碼靜態連結進 DLL,體積較大但無 MFC DLL 相依。

2. 動態連結 MFC 的一般 DLL (Regular DLL Dynamically Linked to MFC)

  • 結構同靜態版,但動態連結 MFCxx.DLL,體積小。
  • 關鍵要求:每個導出函數開頭必須加入:
    AFX_MANAGE_STATE(AfxGetStaticModuleState())
    此宏負責切換模組狀態,確保資源句柄、類別工廠等指向正確模組。詳見第 9 章。

3. 延伸 DLL (Extension DLL)

  • 僅能被「動態連結 MFC」的 MFC 應用程式使用。
  • 無 CWinApp 物件;必須實現 DllMain。
  • DllMain 中呼叫 AfxInitExtensionModule,檢查返回值。
  • 若需導出 CRuntimeClass 或資源,必須提供初始化函數建立 CDynLinkLibrary 物件並導出該函數;宿主 APP 在 InitInstance 中呼叫之。
  • MFC 核心庫 (MFCxx.DLL) 本身即為特殊的延伸 DLL。

三、 DLL 入口點與 CRT 初始化

1. DllMain 結構

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。

2. _DllMainCRTStartup

連結 CRT DLL 版本(多線程)時必須指定為入口點。職責:

  • 進程/線程附加時:配置 CRT 資料、建構全域 C++ 物件。
  • 分離時:清理 CRT 資料、解構全域物件。
  • 呼叫使用者 DllMain 與 RawDllMain(動態連結 MFC 時需靜態連結的輔助函數,處理模組狀態)。

3. 一般 DLL 使用 MFC 的 DllMain

MFC 提供的 DllMain 會自動呼叫 CWinApp::InitInstance/ExitInstance;延伸 DLL 必須自行實現。

四、 導出函數與類別的三種方法

  1. .def 模組定義檔:EXPORTS 區列出導出名(C++ 需用修飾名,可用 dumpbin /SYMBOLS 取得)。支援序號導出(效率最高)。
  2. 連結器 /EXPORT 命令列:直接指定導出符號。
  3. __declspec(dllexport) / MFC 宏 (推薦):
    MFC 定義雙向宏,DLL 端定義 _AFXEXT 時展開為 dllexport,使用端為 dllimport:
    AFX_EXT_CLASS(類別)、AFX_EXT_API(函數)、AFX_EXT_DATA(資料)。
    範例:class AFX_EXT_CLASS CMyClass : public CObject { ... };

建議優先使用第 3 種(維護性最佳),次之 .def(序號導出效率高),最後命令列。

五、 DLL 記憶體與資料共享規則

  • DLL 映射到呼叫進程虛擬空間,記憶體從進程空間配置。
  • DLL 全域變數:每個進程擁有獨立實例(Win32 特性)。
  • 跨線程存取共享資料需同步;需線程私有資料請用 TLS。
  • 句柄可在 DLL 與進程間傳遞;DLL 使用進程堆疊。

六、 模組狀態切換核心:AFX_MANAGE_STATE

動態連結 MFC 的一般 DLL 導出函數必須以此宏開頭。它構造一個 AFX_MODULE_STATE 切換器,將當前線程的模組狀態指標暫時指向該 DLL 的靜態模組狀態(AfxGetStaticModuleState() 返回),函數返回時自動恢復。這確保 AfxGetResourceHandle()、AfxGetApp()、類別工廠映射等均指向 DLL 自身資源,而非宿主 EXE。詳細機制見第 9 章 9.4.1 節。

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