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

MFC 通用控制項初始化機制深度解析

     編輯:關於VC++

前言

閱讀原始碼時發現 MFC 對通用控制項初始化採用延遲載入策略,經研究整理如下,供參考。


InitCommonControls 與 InitCommonControlsEx

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 載入/卸載與視窗類別註冊生命週期

  • 進程首次載入 DLL:DLL_PROCESS_ATTACH 時以 ICC_WIN95_CLASSES 呼叫 InitCommonControlsEx 註冊基礎類別
  • 進程最後卸載 DLL:DLL_PROCESS_DETACH 時呼叫 UnregisterClass 反註冊
  • Win9x: DLL 卸載自動反註冊視窗類別
  • NT/2000/XP: 必須在 DllMain 手動呼叫 UnregisterClass,否則 DLL 卸載後重建控制項會指向無效位址

MFC 延遲載入機制

MFC 採用「首次使用時才載入」策略:若程式未使用通用控制項,完全不載入 comctl32.dll(用 depends 工具觀察可驗證)。

初始化觸發點:PreCreateWindow

以 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);
}

核心實作:_AfxInitCommonControls

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 配對與參考計數

程式碼中 LoadLibrary/FreeLibrary 成對出現,維持 DLL 參考計數不變。但呼叫 InitCommonControlsEx 會導致系統額外一次 LoadLibrary(因 MFC 延遲載入),此額外參考計數在 FreeLibrary 後保留,使視窗類別註冊持久有效。

MFC 偶爾直接呼叫、偶爾取函式位址呼叫 InitCommonControlsEx,僅為相容不同連結選項。

總結

MFC 透過 PreCreateWindow 延遲初始化通用控制項,配合 AfxDeferRegisterClass → _AfxInitCommonControls → LoadLibrary + InitCommonControlsEx + FreeLibrary 機制,實現按需載入、避免不必要的 DLL 相依。理解此機制有助於除錯控制項建立失敗、DLL 版本衝突等問題。

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