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

VC++ DLL 編程深度解析(三):MFC Regular DLL 與資源模塊狀態切換

     編輯:VC++

重點:MFC Regular DLL 創建向導、接口導出、共享 MFC DLL 時的資源句柄切換三大法

1. MFC Regular DLL 核心特徵

  • 包含 CWinApp 派生類(但無 Run() 消息泵)。
  • 可內部使用 MFC 類(CDialog、CString 等)。
  • 導出接口必須為 C 風格函數(不能導出 MFC 類),以便非 MFC 調用者使用。
  • 分兩種鏈接模式:
    • 靜態鏈接 MFC:MFC 代碼複製進 DLL,體積大 (~300KB+),無資源衝突,無需切換模塊狀態。
    • 動態鏈接 MFC:共享 MFCxx.dll,體積小 (~20KB),但資源 ID 衝突風險極高,必須切換模塊狀態。

2. 向導創建步驟

  1. New Project → MFC AppWizard (dll) → 名稱 RegularDLL。
  2. Step 1:選擇 Regular DLL using shared MFC DLL (動態鏈接) 或 Regular DLL with MFC statically linked。
  3. Step 2-4:Automation、Windows Sockets、Comments 可選。
  4. 生成骨架:CRegularDLLApp : public CWinApp + theApp 實例。

3. 簡易示例:導出模態對話框


// RegularDLL.h (App 類)
class CRegularDLLApp : public CWinApp {
public:
    CRegularDLLApp();
    DECLARE_MESSAGE_MAP()
};

// RegularDLL.cpp
CRegularDLLApp::CRegularDLLApp() {}
BEGIN_MESSAGE_MAP(CRegularDLLApp, CWinApp) END_MESSAGE_MAP()
CRegularDLLApp theApp;

// Dlg.h / Dlg.cpp:ClassWizard 添加 CDialog 派生類 CDlgDemo,IDD = IDD_DLL_DIALOG

// Interface.cpp:導出接口
#include "stdafx.h"
#include "Dlg.h"

extern "C" __declspec(dllexport) void ShowDllDialog() {
    CDlgDemo dlg;
    dlg.DoModal();
}

4. 調用方測試


// 呼叫端:MFC 對話框程序 RegularDLLCall
void CRegularDLLCallDlg::OnCallDll() {
    typedef void (*PFN_SHOW)();
    HMODULE hDll = LoadLibrary(_T("RegularDLL.dll"));
    if (hDll) {
        PFN_SHOW pFn = (PFN_SHOW)GetProcAddress(hDll, "ShowDllDialog");
        if (pFn) pFn();
        FreeLibrary(hDll);
    }
}

5. 共享 MFC DLL 的致命陷阱:資源 ID 衝突

現象:DLL 與 EXE 都有 IDD_DIALOG = 2000。調用 ShowDllDialog() 彈出的是 EXE 的對話框,而非 DLL 的。

根因:MFC 共享版默認使用 EXE 的資源模塊句柄 (AfxGetResourceHandle()) 加載資源。若 DLL 與 EXE 存在同 ID 資源,將加載錯誤資源。

6. 三種資源模塊狀態切換方案

方法一:AFX_MANAGE_STATE 宏(最推薦,RAII 風格)


extern "C" __declspec(dllexport) void ShowDllDialog() {
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 函數入口第一行
    
    CDlgDemo dlg;
    dlg.DoModal();
} // 離開作用域自動恢復

原理:AfxGetStaticModuleState() 返回 DLL 靜態 AFX_MODULE_STATE 結構指針(含 m_hCurrentResourceHandle = DLL 的 hInstance)。宏在棧上構造臨時對象,構造時保存舊狀態並設置新狀態,析構時自動恢復。

方法二:手動 AfxGet/SetResourceHandle(靈活,可中途多次切換)


extern "C" __declspec(dllexport) void ShowDllDialog() {
    HINSTANCE hOld = AfxGetResourceHandle();
    AfxSetResourceHandle(theApp.m_hInstance); // 切到 DLL 資源
    
    CDlgDemo dlg1; dlg1.DoModal(); // 使用 DLL 資源
    
    AfxSetResourceHandle(hOld);    // 恢復 EXE 資源
    
    CDlgDemo dlg2; dlg2.DoModal(); // 使用 EXE 資源 (若 EXE 也有同 ID)
}

方法三:調用方主動切換(不修改 DLL 代碼)


void CRegularDLLCallDlg::OnCallDll() {
    HINSTANCE hExe = GetModuleHandle(NULL);
    HINSTANCE hDll = GetModuleHandle(_T("RegularDLL.dll"));
    
    AfxSetResourceHandle(hDll);
    ShowDllDialog(); // 彈出 DLL 對話框
    
    AfxSetResourceHandle(hExe);
    ShowDllDialog(); // 彈出 EXE 對話框
}

7. 靜態鏈接 MFC 時為何不需要切換?

靜態鏈接時,DLL 內部擁有獨立的 MFC 代碼副本,AfxGetResourceHandle() 直接返回 DLL 自己的模塊句柄,不共享 EXE 的資源鏈,自然無衝突。代價是體積膨脹且多 DLL 間 MFC 狀態不共享(如全局內存分配器)。

8. 選型建議

場景推薦類型理由
純算法/數據處理,無 UI 資源Non-MFC DLL最輕量、無 MFC 依賴、跨語言最易
需導出 MFC 對話框/控件,給 MFC/非 MFC 調用Regular DLL (動態鏈接)體積小,配合 AFX_MANAGE_STATE 解決資源衝突
需導出 CObject 派生類,僅供 MFC 應用使用Extension DLL支持多態、動態創建、序列化,下一節講解
插件系統,主程序與插件同屬一開發組Extension DLL共享 MFC 狀態,對象指針可直接跨模塊傳遞

下一節預告

MFC Extension DLL:CDynLinkLibrary 機制、導出 CObject 派生類、DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE 跨模塊動態創建、版本兼容策略。

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