重點:MFC Regular DLL 創建向導、接口導出、共享 MFC DLL 時的資源句柄切換三大法
CWinApp 派生類(但無 Run() 消息泵)。CDialog、CString 等)。MFCxx.dll,體積小 (~20KB),但資源 ID 衝突風險極高,必須切換模塊狀態。RegularDLL。CRegularDLLApp : public CWinApp + theApp 實例。
// 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();
}
// 呼叫端: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);
}
}
現象:DLL 與 EXE 都有 IDD_DIALOG = 2000。調用 ShowDllDialog() 彈出的是 EXE 的對話框,而非 DLL 的。
根因:MFC 共享版默認使用 EXE 的資源模塊句柄 (AfxGetResourceHandle()) 加載資源。若 DLL 與 EXE 存在同 ID 資源,將加載錯誤資源。
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)
}
void CRegularDLLCallDlg::OnCallDll() {
HINSTANCE hExe = GetModuleHandle(NULL);
HINSTANCE hDll = GetModuleHandle(_T("RegularDLL.dll"));
AfxSetResourceHandle(hDll);
ShowDllDialog(); // 彈出 DLL 對話框
AfxSetResourceHandle(hExe);
ShowDllDialog(); // 彈出 EXE 對話框
}
靜態鏈接時,DLL 內部擁有獨立的 MFC 代碼副本,AfxGetResourceHandle() 直接返回 DLL 自己的模塊句柄,不共享 EXE 的資源鏈,自然無衝突。代價是體積膨脹且多 DLL 間 MFC 狀態不共享(如全局內存分配器)。
| 場景 | 推薦類型 | 理由 |
|---|---|---|
| 純算法/數據處理,無 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 跨模塊動態創建、版本兼容策略。