閱讀原始碼時發現 MFC 對通用控制項初始化採用延遲載入策略,經研究整理如下,供參考。
Win95 起 Windows 提供通用控制項 (Toolbar、Status bar、Tree view、List view 等),程式碼位於 comctl32.dll。使用前必須初始化(註冊視窗類別)。
void InitCommonControls(VOID);
BOOL InitCommonControlsEx(LPINITCOMMONCONTROLSEX lpInitCtrls);
InitCommonControls:無參數,初始化大部分通用控制項(實為空函式,透過連結器載入 DLL 觸發 DLL 入口點自動註冊)InitCommonControlsEx:可指定初始化哪些控制項類別#define ICC_LISTVIEW_CLASSES 0x00000001 // listview, header
#define ICC_TREEVIEW_CLASSES 0x00000002 // treeview, tooltips
#define ICC_BAR_CLASSES 0x00000004 // toolbar, statusbar, trackbar, tooltips
#define ICC_TAB_CLASSES 0x00000008 // tab, tooltips
#define ICC_UPDOWN_CLASS 0x00000010 // updown
#define ICC_PROGRESS_CLASS 0x00000020 // progress
#define ICC_HOTKEY_CLASS 0x00000040 // hotkey
#define ICC_ANIMATE_CLASS 0x00000080 // animate
#define ICC_WIN95_CLASSES 0x000000FF // 以上所有
#define ICC_DATE_CLASSES 0x00000100 // month/date/time picker, updown
#define ICC_USEREX_CLASSES 0x00000200 // comboex
#define ICC_COOL_CLASSES 0x00000400 // rebar (coolbar)
#define ICC_INTERNET_CLASSES 0x00000800
#define ICC_PAGESCROLLER_CLASS 0x00001000 // page scroller
#define ICC_NATIVEFNTCTL_CLASS 0x00002000 // native font control
DLL_PROCESS_ATTACH 時以 ICC_WIN95_CLASSES 呼叫 InitCommonControlsEx 註冊基礎類別DLL_PROCESS_DETACH 時呼叫 UnregisterClass 反註冊DllMain 手動呼叫 UnregisterClass,否則 DLL 卸載後重建控制項會指向無效位址MFC 採用「首次使用時才載入」策略:若程式未使用通用控制項,完全不載入 comctl32.dll(用 depends 工具觀察可驗證)。
以 CListView (包裝 syslistview32) 為例:
BOOL CListView::PreCreateWindow(CREATESTRUCT& cs)
{
return CCtrlView::PreCreateWindow(cs);
}
BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs)
{
// ...
VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
// ...
return CView::PreCreateWindow(cs);
}
LONG AFXAPI _AfxInitCommonControls(LPINITCOMMONCONTROLSEX lpInitCtrls, LONG fToRegister)
{
// ...
HINSTANCE hInst = ::LoadLibraryA("COMCTL32.DLL");
// ...
(FARPROC&)pfnInit = ::GetProcAddress(hInst, "InitCommonControlsEx");
if (pfnInit == NULL) { /* 錯誤處理 */ }
else if (InitCommonControlsEx(lpInitCtrls)) { /* 成功 */ }
FreeLibrary(hInst);
// ...
}
程式碼中 LoadLibrary/FreeLibrary 成對出現,維持 DLL 參考計數不變。但呼叫 InitCommonControlsEx 會導致系統額外一次 LoadLibrary(因 MFC 延遲載入),此額外參考計數在 FreeLibrary 後保留,使視窗類別註冊持久有效。
MFC 偶爾直接呼叫、偶爾取函式位址呼叫 InitCommonControlsEx,僅為相容不同連結選項。
MFC 透過 PreCreateWindow 延遲初始化通用控制項,配合 AfxDeferRegisterClass → _AfxInitCommonControls → LoadLibrary + InitCommonControlsEx + FreeLibrary 機制,實現按需載入、避免不必要的 DLL 相依。理解此機制有助於除錯控制項建立失敗、DLL 版本衝突等問題。