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

MFC 內部運作機制深度解析:從 WinMain 到訊息迴圈

     編輯:關於VC++

前言

MFC 將 Windows API 封裝為 C++ 類別,掩蓋了底層細節。理解 MFC 內部啟動流程、視窗類別註冊、文檔/視圖架構建立、訊息迴圈運作,有助於在正確位置覆寫虛擬函數、添加訊息處理。本文追蹤原始碼路徑,完整還原 MFC 應用程式生命週期。

MFC 應用程式啟動完整呼叫堆疊


theApp (全域物件建構)
    ↓
WinMain() (C:\VC98\MFC\SRC\APPMODUL.CPP)
    ↓
AfxWinMain() (WINMAIN.CPP)
    ↓
AfxWinInit() → MFC 內部初始化
    ↓
pApp->InitApplication() → CWinApp::InitApplication (鮮用)
    ↓
pThread->InitInstance() → CWinApp::InitInstance (關鍵)
    ↓
    應用程式覆寫的 InitInstance()
        ↓
        1. 文檔模板建立 (CSingleDocTemplate 等)
        2. 主框架視窗建立
            ↓
            LoadFrame() → CFrameWnd::LoadFrame
                ↓
                AfxDeferRegisterClass() → 註冊視窗類別
                ↓
                CFrameWnd::Create() → CWnd::CreateEx()
                    ↓
                    PreCreateWindow() (虛擬函數,可覆寫修改視窗樣式)
        3. ShowWindow() / UpdateWindow()
    ↓
CWinThread::Run() → 訊息迴圈
    ↓
    do-while 迴圈
        ↓
        PumpMessage()
            ↓
            GetMessage() → TranslateMessage() → DispatchMessage()

關鍵原始碼追蹤路徑 (VC98 安裝目錄)

  • WinMain:MFC\SRC\APPMODUL.CPP
  • AfxWinMain:MFC\SRC\WINMAIN.CPP
  • CWinThread::Run:MFC\SRC\THRDCORE.CPP
  • CFrameWnd::LoadFrame:MFC\SRC\FRAMEWnd.CPP
  • AfxEndDeferRegisterClass:MFC\SRC\WINMAIN.CPP (含各類視窗類別註冊邏輯)

核心機制詳細解析

1. 視窗類別延遲註冊 AfxDeferRegisterClass

MFC 不在啟動時一次註冊所有類別,而是依需求延遲註冊:

  • AFX_WND_REG:一般子視窗
  • AFX_WNDFRAMEORVIEW_REG:框架/視圖視窗 (白底、雙擊)
  • AFX_WNDMDIFRAME_REG:MDI 框架
  • AFX_WNDCONTROLBAR_REG:控制列
  • AFX_WNDOLECONTROL_REG:OLE 控制項
  • AFX_WNDCOMMCTLS_REG 等:Common Controls (TreeView, ListView...)

LoadFrame 內呼叫 AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG) 確保框架視窗類別已註冊。

2. PreCreateWindow 虛擬函數鏈


CWnd::CreateEx()
    ↓
CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) // 設定預設樣式
    ↓
CMainFrame::PreCreateWindow(CREATESTRUCT& cs) // 應用程式覆寫,修改 cs.style/cs.lpszClass

CREATESTRUCT 包含視窗建立所有參數:x, y, cx, cy, style, dwExStyle, lpszClass, lpszName, hMenu, hInstance。覆寫此函數可在建立前調整視窗屬性 (如移除 WS_MAXIMIZEBOX、變更類別名稱)。

3. 文檔/視圖架構建立

InitInstance 中:

  1. 建立 CSingleDocTemplate (關聯 Document, Frame, View 類別)
  2. AddDocTemplate 加入應用程式
  3. ParseCommandLine / ProcessShellCommand 處理命令列 (新建/開啟/列印/ DDE)
  4. 建立主框架視窗、文檔、視圖三者關聯

4. 訊息迴圈 CWinThread::Run


int CWinThread::Run()
{
    MSG msg;
    BOOL bIdle = TRUE;
    LONG lIdleCount = 0;

    for (;;) // 無限迴圈
    {
        // Phase 1: 處理閒置時間
        while (bIdle && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if (!OnIdle(lIdleCount++))
                bIdle = FALSE; // 無閒置工作
        }

        // Phase 2: 泵送訊息
        do
        {
            if (!PumpMessage()) // GetMessage → Translate → Dispatch
                return ExitInstance(); // WM_QUIT

            if (IsIdleMessage(&msg)) // 某些訊息重置閒置狀態
            {
                bIdle = TRUE;
                lIdleCount = 0;
            }
        } while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE));
    }
}

OnIdle 可覆寫執行背景工作 (更新 UI 狀態、自動儲存等)。

5. 視窗銷毀與物件生命週期

  • 視窗銷毀 (DestroyWindow) ≠ C++ 物件銷毀
  • CWnd 物件與 HWND 關聯:m_hWnd 為 0 表示未附加
  • 物件銷毀時必須確保視窗已銷毀;視窗銷毀不強制銷毀物件
  • PostNcDestroy 中預設 delete this (針對動態配置的框架/視圖)

實務指導:為什麼要在特定位置添加程式碼?

需求正確位置原因
修改主視窗樣式/類別CMainFrame::PreCreateWindow視窗建立前最後修改 CREATESTRUCT 機會
初始化文檔/視圖資料CDocument::OnNewDocument / CView::OnInitialUpdate文檔/視圖建立完成後的標準入口
處理特定命令訊息訊息映射 ON_COMMANDMFC 命令路由機制自動分派
背景處理/更新 UI 狀態CWinApp::OnIdle訊息迴圈閒置階段自動呼叫
應用程式級初始化CWinApp::InitInstance單一執行緒應用程式的主要初始化點

結語

MFC 內部機制雖複雜,但核心脈絡清晰:theApp 建構 → WinMain → AfxWinMain → InitInstance (建立文檔/視圖/框架) → Run 訊息迴圈。掌握 PreCreateWindow、OnIdle、LoadFrame、視窗類別註冊等關鍵節點,即能在正確層級擴展功能,避免「在錯誤地方加程式碼」導致的難以除錯問題。

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