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

VC++ 深度解析 MFC 框架視窗建立流程 (二):CreateEx、PreCreateWindow 與視窗顯示

     編輯:VC 教學

前言

承接上篇「MFC 框架視窗註冊與類別設計」,本文深入剖析 CWnd::CreateEx、CFrameWnd::Create、虛函數 PreCreateWindow 如何協同完成視窗建立、樣式修改,以及 ShowWindow/UpdateWindow 顯示更新機制。

核心函數呼叫鏈

CWinApp::InitInstance
  → CFrameWnd::Create (或衍生類覆寫)
    → CWnd::CreateEx
      → PreCreateWindow (虛函數,可修改 CREATESTRUCT)
      → AfxHookWindowCreate (MFC 內部掛鉤)
      → ::CreateWindowEx (Win32 API 真正建立 HWND)
      → OnCreate (WM_CREATE 處理)
  → m_pMainWnd->ShowWindow(SW_SHOW)
  → m_pMainWnd->UpdateWindow()

CWnd::CreateEx 關鍵原始碼解析 (WINCORE.CPP)

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
    LPCTSTR lpszWindowName, DWORD dwStyle,
    int x, int y, int nWidth, int nHeight,
    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
    CREATESTRUCT cs;
    cs.dwExStyle = dwExStyle;
    cs.lpszClass = lpszClassName;
    cs.lpszName  = lpszWindowName;
    cs.style     = dwStyle;
    cs.x = x; cs.y = y; cs.cx = nWidth; cs.cy = nHeight;
    cs.hwndParent = hWndParent; cs.hMenu = nIDorHMenu;
    cs.hInstance = AfxGetInstanceHandle(); cs.lpCreateParams = lpParam;

    // 關鍵:呼叫虛函數 PreCreateWindow,允許衍生類修改 cs
    if (!PreCreateWindow(cs)) {
        PostNcDestroy(); return FALSE;
    }

    AfxHookWindowCreate(this); // MFC 內部:將 HWND 與 CWnd* 關聯
    HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName,
        cs.style, cs.x, cs.y, cs.cx, cs.cy,
        cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
    // ... 後續附加處理
    return (hWnd != NULL);
}

CFrameWnd::Create 實作 (WINFRM.CPP)

BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
    DWORD dwStyle, const RECT& rect, CWnd* pParentWnd,
    LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext)
{
    // ... 處理選單、Context 等 ...
    if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
        rect.left, rect.top, rect.Width(), rect.Height(),
        pParentWnd ? pParentWnd->GetSafeHwnd() : NULL, hMenu,
        (LPVOID)pContext))
    {
        TRACE0("Warning: failed to create CFrameWnd.\n");
        if (hMenu) DestroyMenu(hMenu);
        return FALSE;
    }
    // ... 後續初始化 ...
    return TRUE;
}

PreCreateWindow:視窗樣式客製化的關鍵

  • 定義於 CWnd 為 virtual BOOL PreCreateWindow(CREATESTRUCT& cs)。
  • CFrameWnd::PreCreateWindow 預設註冊 _afxWndFrameOrView 類別、加入 WS_EX_CLIENTEDGE 等。
  • 衍生類 (如 CMainFrame) 覆寫 即可在建立前修改樣式:移除最大化按鈕、調整邊框、變更類別等。

實例:移除最大化/最小化按鈕

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if (!CFrameWnd::PreCreateWindow(cs)) return FALSE;
    // 移除最大化、最小化、系統選單 (保留關閉)
    cs.style &= ~(WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU);
    // 或僅移除最大化
    // cs.style &= ~WS_MAXIMIZEBOX;
    return TRUE;
}

CREATESTRUCT 與 CreateWindowEx 參數一一對應

CREATESTRUCT 欄位CreateWindowEx 參數說明
dwExStyledwExStyle擴充視窗樣式 (WS_EX_*)
lpszClasslpClassName視窗類別名稱
lpszNamelpWindowName視窗標題
styledwStyle視窗樣式 (WS_*)
x, yX, Y左上角座標
cx, cynWidth, nHeight寬、高
hwndParenthWndParent父視窗
hMenuhMenu選單/子視窗 ID
hInstancehInstance模組實例
lpCreateParamslpParamWM_CREATE 的 lpCreateParams

順序相反:結構體欄位順序與 API 參數順序相反,但語義完全對應。

視窗顯示與更新

// CWinApp::InitInstance 典型結尾
m_pMainWnd->ShowWindow(SW_SHOW);   // 發送 WM_SHOWWINDOW,設定可見狀態
m_pMainWnd->UpdateWindow();        // 發送 WM_PAINT,強制立即繪製客戶區
  • ShowWindow:控制視窗顯示狀態 (SW_SHOW, SW_HIDE, SW_MINIMIZE...)。
  • UpdateWindow:若更新區域非空,繞過訊息佇列直接發送 WM_PAINT,確保首次繪製即時呈現。

調試技巧

  1. 在 CMainFrame::PreCreateWindow 設中斷點,觀察 cs 修改前後差異。
  2. 在 CWnd::CreateEx 與 CFrameWnd::Create 同設中斷點,驗證呼叫順序。
  3. 使用 Spy++ 檢視最終 HWND 樣式、類別名稱、擴充樣式是否符合預期。

總結

MFC 視窗建立核心在於 CreateEx → PreCreateWindow → CreateWindowEx 三部曲。PreCreateWindow 為開發者保留了修改視窗類別、樣式、位置的最後機會,善用它可避免建立後再 ModifyStyle 的額外開銷與閃爍。

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