MFC 文檔/視圖架構的靈魂在於「對象創建與協作」。本文系統梳理 應用程序、文檔模板、文檔、框架窗口、視圖 五大核心對象的創建關系、交互接口、關鍵數據成員,並以前 4 章機制(動態創建、消息映射、命令路由)為基礎,完整還原 SDI 應用程序從 WinMain → InitInstance → Run 消息循環 → 對象創建 → 窗口顯示的啟動全流程,為深度掌握 MFC 框架打下堅實基礎。
| 創建者 | 被創建對象 | 關鍵函數 |
|---|---|---|
應用程序對象 (CWinApp) | 文檔模板 (CDocTemplate) | AddDocTemplate |
| 文檔模板 | 文檔 (CDocument) | CreateNewDocument / OpenDocumentFile |
| 文檔模板 | 框架窗口 (CFrameWnd/CMDIChildWnd) | CreateNewFrame → LoadFrame |
| 框架窗口 | 視圖 (CView) | OnCreateClient → CreateView |
| 當前對象 | 目標對象 | 成員函數 |
|---|---|---|
CDocument | 視圖列表 | GetFirstViewPosition / GetNextView |
CDocument | 文檔模板 | GetDocTemplate |
CView | 文檔 | GetDocument |
CView | 框架窗口 | GetParentFrame |
CFrameWnd/CMDIChildWnd | 活動視圖 | GetActiveView |
CMDIFrameWnd | 活動子框架 | MDIGetActive |
GetDocument()->UpdateAllViews(pSender, lHint, pHint) → 所有關聯視圖 OnUpdate。SetActiveView(pView, bNotify) → 視圖 OnActivateView。| 類 | 關鍵虛擬函數 | 覆蓋目的 |
|---|---|---|
CWinThread | InitInstance, ExitInstance, Run, OnIdle, PreTranslateMessage | 線程初始化/退出/消息循環/空閒處理/消息預處理 |
CWinApp | InitInstance, ExitInstance, OnFileNew/Open, SaveModifiedDocument, DoMessageBox | **必覆蓋** 應用程序初始化;文件命令/保存提示/訊息框定制 |
CFrameWnd | OnCreateClient, ActivateFrame, LoadFrame, GetMessageBar | 視圖創建策略、窗口激活方式、框架加載、狀態欄訪問 |
CMDIFrameWnd | CreateClient, GetWindowMenuPopup | MDI Client 窗口創建、Window 菜單彈出項獲取 |
CView | OnDraw, OnInitialUpdate, OnUpdate, OnPrepareDC, OnPrint, OnActivateView/Frame | **核心繪製**、初始化、增量更新、打印、激活響應 |
CDocument | OnNewDocument, OnOpenDocument, OnSaveDocument, DeleteContents, Serialize | 文檔生命周期、序列化、內容清理 |
CDocTemplate | CreateNewDocument, CreateNewFrame, InitialUpdateFrame, OpenDocumentFile, MatchDocType | 文檔/框架/視圖創建工廠、打開邏輯、類型匹配度 |
註:覆蓋虛擬函數通常需調用基類實現以保持框架默認行為。
MFC 為常用菜單命令提供默認實現,路由至特定類:
ID_FILE_NEW/OPEN → CWinApp::OnFileNew/OpenID_FILE_SAVE/SAVE_AS → CDocument::OnFileSave/SaveAsID_FILE_PRINT/PRINT_PREVIEW → CView::OnFilePrint/PrintPreviewID_APP_EXIT → CWinApp::OnAppExitID_VIEW_TOOLBAR/STATUS_BAR → CFrameWnd 處理顯示/隱藏ID_WINDOW_NEW/CASCADE/TILE → CMDIFrameWnd 管理 MDI 子窗口程序員可通過類向導映射至派生類覆蓋,建議遵循默認路由類。
int AFXAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow) {
// 1. 全局應用程序對象已構造
// 2. AFX 模塊狀態初始化
// 3. 調用 pThread->InitInstance() -- 必須覆蓋,返回 FALSE 則退出
// 4. 調用 pThread->Run() -- 進入消息循環
// 5. 調用 pThread->ExitInstance() -- 清理
// 6. 返回退出碼
}
BOOL CWinThread::Run() {
while (PumpMessage()); // 循環獲取/分發消息
return TRUE;
}
BOOL CWinThread::PumpMessage() {
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) return FALSE;
PreTranslateMessage(&msg); // 虛擬函數,預處理(加速鍵、工具提示)
::TranslateMessage(&msg);
::DispatchMessage(&msg);
} else {
// 無消息時進入空閒處理
long lIdle = 0;
while (OnIdle(lIdle++)) ; // 虛擬函數,更新 UI 狀態等
}
return TRUE;
}
關鍵點:PreTranslateMessage 由 WalkPreTranslateTree 向上遞歸調用目標窗口及其祖先的預處理;OnIdle 返回 TRUE 表示繼續空閒處理,FALSE 結束本輪空閒。
BOOL CMyApp::InitInstance() {
// ① 創建並註冊單文檔模板
CSingleDocTemplate* pTemplate = new CSingleDocTemplate(
IDR_MAINFRAME, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CMyView));
AddDocTemplate(pTemplate);
// ② 解析命令行並執行 Shell 命令(新建/打開/打印)
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo); // 填充 m_nShellCommand (FileNew/FileOpen...), m_strFileName
if (!ProcessShellCommand(cmdInfo)) return FALSE; // 核心創建入口
// ③ 返回 TRUE,WinMain 將調用 Run 進入消息循環
return TRUE;
}
CCommandLineInfo 封裝解析結果:m_nShellCommand (FileNew/FileOpen/FilePrint...)、m_strFileName。m_nShellCommand = FileNew,無參數啟動即新建文檔。ProcessShellCommand 根據命令類型調用:
FileNew → OnFileNew → 文檔模板 OpenDocumentFile(NULL)FileOpen → OnFileOpen → 彈出對話框獲取文件名 → 應用程序 OpenDocumentFile → 文檔模板 OpenDocumentFile流程圖解 (圖 5-8):
OnCloseDocument)。CreateNewDocument → CRuntimeClass::CreateObject → CDocument 構造)。CreateNewFrame → CFrameWnd::LoadFrame → HWND 創建)。OnCreate → OnCreateClient → CreateView 創建視圖對象及 HWND。NULL 文件名調用 OnNewDocument(初始化數據結構);有文件名調用 OnOpenDocument → CArchive 序列化讀取。InitialUpdateFrame(pDoc, TRUE) → 視圖 OnInitialUpdate、窗口激活顯示、標題更新。命令行分析 → CWinApp::OnFileNew
→ CDocTemplate::OpenDocumentFile(NULL)
→ CreateNewDocument() → CDocument::OnNewDocument()
→ CreateNewFrame() → CFrameWnd::LoadFrame()
→ CFrameWnd::Create() → ::CreateWindowEx()
→ WM_CREATE → CFrameWnd::OnCreate()
→ OnCreateHelper() → OnCreateClient()
→ CreateView() → CView::Create() → HWND 視圖窗口
→ InitialUpdateFrame() → CView::OnInitialUpdate() → OnUpdate()
→ CFrameWnd::ActivateFrame() → 窗口顯示
m_pDocManager (CDocManager*):文檔模板管理器,實際持有模板列表。m_pRecentFileList (CRecentFileList*):最近文件列表(MRU)。m_nCmdShow, m_lpCmdLine:啟動參數。m_strTitle, m_strPathName:標題/路徑。m_pDocTemplate:反向指針。m_viewList (CPtrList):關聯視圖列表。m_bModified:髒標記,決定關閉時是否提示保存。m_nIDResource:共享資源 ID(菜單/工具欄/圖標/字符串)。m_pDocClass, m_pFrameClass, m_pViewClass:三大類的 CRuntimeClass*。m_strDocStrings:7 段字符串(窗口標題、默認文檔名、文件類型描述、過濾器、擴展名、註冊表 ID/Name)。m_pOnlyDoc 指向唯一文檔。MDI 獨有:m_docList 文檔列表、m_hMenuShared/m_hAccelTable 共享菜單/加速鍵。掌握上述創建鏈、接口表、數據成員,即掌握了 MFC 框架的「骨架與經絡」,後續所有業務邏輯填充皆建立於此基石之上。