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

VC++ 深度解析 MFC 框架視窗註冊與類別設計 (一):AfxEndDeferRegisterClass 與預定義視窗類別

     編輯:VC 教學

前言

MFC 應用程式啟動後,首要任務是註冊視窗類別。MFC 內建多種預定義視窗類別 (子視窗、OLE 控件、MDI/SDI 框架、分割視窗等),由 AfxEndDeferRegisterClass 統一註冊。本文詳細剖析其原始碼邏輯、AfxRegisterClass 封裝、以及 CMainFrame::PreCreateWindow 如何觸發註冊。

AfxEndDeferRegisterClass 核心邏輯 (WINCORE.CPP)

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
    WNDCLASS wndcls;
    memset(&wndcls, 0, sizeof(WNDCLASS));
    wndcls.lpfnWndProc = DefWindowProc;          // 預設視窗程序
    wndcls.hInstance = AfxGetInstanceHandle();   // 當前模組實例
    wndcls.hCursor = afxData.hcurArrow;          // 預設箭頭游標
    // ... hbrBackground, lpszMenuName 等稍後依類別設定

    // 位元旗標決定註冊哪些類別
    if (fToRegister & AFX_WND_REG) {
        // 一般子視窗:無背景筆刷、無圖示、雙擊/重繪樣式
        wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        wndcls.lpszClassName = _afxWnd;          // "Afx:xxxxxx:xxxxx"
        if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WND_REG;
    }

    if (fToRegister & AFX_WNDOLECONTROL_REG) {
        // OLE 控件:使用父 DC 加速
        wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        wndcls.lpszClassName = _afxWndOleControl;
        if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WNDOLECONTROL_REG;
    }

    if (fToRegister & AFX_WNDMDIFRAME_REG) {
        // MDI 框架 / 分割視窗:無背景筆刷,僅雙擊
        wndcls.style = CS_DBLCLKS;
        wndcls.hbrBackground = NULL;
        if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME))
            fRegisteredClasses |= AFX_WNDMDIFRAME_REG;
    }

    if (fToRegister & AFX_WNDFRAMEORVIEW_REG) {
        // SDI 框架 / MDI 子視窗 / View:系統視窗背景色
        wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        wndcls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))
            fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
    }
    // ... 其他類別 (OleDocIPFrame 等)
    return TRUE;
}

AfxRegisterClass 封裝細節

BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
    WNDCLASS wndcls;
    // 1. 若已註冊同名類別,直接成功
    if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName, &wndcls))
        return TRUE;

    // 2. 呼叫 Win32 API 註冊
    if (!::RegisterClass(lpWndClass)) {
        TRACE1("Can't register window class named %s\n", lpWndClass->lpszClassName);
        return FALSE;
    }

    // 3. DLL 專案需記錄以便卸載時反註冊
    if (afxContextIsDLL) {
        AfxLockGlobals(CRIT_REGCLASSLIST);
        // 將類別名追加至模組狀態的 m_szUnregisterList
        // ... 字串操作 ...
        AfxUnlockGlobals(CRIT_REGCLASSLIST);
    }
    return TRUE;
}

觸發註冊的關鍵:AfxDeferRegisterClass 巨集

// AFXIMPL.H
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

CFrameWnd::PreCreateWindow 中呼叫:

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
    if (cs.lpszClass == NULL) {
        VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
        cs.lpszClass = _afxWndFrameOrView; // 使用系統視窗背景色的類別
    }
    // ... 樣式微調 ...
    return TRUE;
}

CMainFrame::PreCreateWindow 呼叫鏈

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if (!CFrameWnd::PreCreateWindow(cs)) return FALSE;
    // TODO: 修改 cs.style, cs.lpszClass 等
    return TRUE;
}

執行順序:WinMain → AfxWinMain → CWinApp::InitInstance → CFrameWnd::Create → CWnd::CreateEx → PreCreateWindow (CMainFrame → CFrameWnd) → AfxDeferRegisterClass → 註冊類別。

MFC 預定義視窗類別一覽

旗標常數類別名稱變數用途關鍵樣式/背景
AFX_WND_REG_afxWnd一般子視窗CS_DBLCLKS\|CS_HREDRAW\|CS_VREDRAW, 無背景
AFX_WNDOLECONTROL_REG_afxWndOleControlOLE 控件+ CS_PARENTDC
AFX_WNDMDIFRAME_REG_afxWndMDIFrameMDI 框架/分割視窗CS_DBLCLKS, 無背景, 標準圖示
AFX_WNDFRAMEORVIEW_REG_afxWndFrameOrViewSDI 框架/MDI 子視窗/ViewCS_DBLCLKS\|CS_HREDRAW\|CS_VREDRAW, COLOR_WINDOW+1

調試驗證技巧

  1. 在 CMainFrame::PreCreateWindow 設中斷點,F5 執行,呼叫堆疊將顯示完整註冊路徑。
  2. 使用 Spy++ 檢視主視窗 Class Name,應為 Afx:xxxxxx:xxxxxx:xxxxxx 格式 (即 _afxWndFrameOrView 實際名稱)。
  3. 若需自訂視窗類別 (如特殊背景筆刷、游標),在 CMainFrame::PreCreateWindow 中自行 AfxRegisterClass 並指派 cs.lpszClass。

總結

MFC 透過 AfxEndDeferRegisterClass 延遲註冊,按需載入預定義視窗類別。CFrameWnd::PreCreateWindow 為 SDI/MDI 框架選擇 _afxWndFrameOrView,開發者可在衍生類 PreCreateWindow 中攔截修改。理解此機制,有助於解決視窗類別衝突、自訂背景繪製、DLL 視窗類別隔離等進階問題。

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