MFC 將 Windows API 封裝為 C++ 類別,掩蓋了底層細節。理解 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()
WinMain:MFC\SRC\APPMODUL.CPPAfxWinMain:MFC\SRC\WINMAIN.CPPCWinThread::Run:MFC\SRC\THRDCORE.CPPCFrameWnd::LoadFrame:MFC\SRC\FRAMEWnd.CPPAfxEndDeferRegisterClass:MFC\SRC\WINMAIN.CPP (含各類視窗類別註冊邏輯)AfxDeferRegisterClassMFC 不在啟動時一次註冊所有類別,而是依需求延遲註冊:
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) 確保框架視窗類別已註冊。
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、變更類別名稱)。
InitInstance 中:
CSingleDocTemplate (關聯 Document, Frame, View 類別)AddDocTemplate 加入應用程式ParseCommandLine / ProcessShellCommand 處理命令列 (新建/開啟/列印/ DDE)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 狀態、自動儲存等)。
DestroyWindow) ≠ C++ 物件銷毀CWnd 物件與 HWND 關聯:m_hWnd 為 0 表示未附加PostNcDestroy 中預設 delete this (針對動態配置的框架/視圖)| 需求 | 正確位置 | 原因 |
|---|---|---|
| 修改主視窗樣式/類別 | CMainFrame::PreCreateWindow | 視窗建立前最後修改 CREATESTRUCT 機會 |
| 初始化文檔/視圖資料 | CDocument::OnNewDocument / CView::OnInitialUpdate | 文檔/視圖建立完成後的標準入口 |
| 處理特定命令訊息 | 訊息映射 ON_COMMAND | MFC 命令路由機制自動分派 |
| 背景處理/更新 UI 狀態 | CWinApp::OnIdle | 訊息迴圈閒置階段自動呼叫 |
| 應用程式級初始化 | CWinApp::InitInstance | 單一執行緒應用程式的主要初始化點 |
MFC 內部機制雖複雜,但核心脈絡清晰:theApp 建構 → WinMain → AfxWinMain → InitInstance (建立文檔/視圖/框架) → Run 訊息迴圈。掌握 PreCreateWindow、OnIdle、LoadFrame、視窗類別註冊等關鍵節點,即能在正確層級擴展功能,避免「在錯誤地方加程式碼」導致的難以除錯問題。