承接上篇「Win32 API 領域模型」,本文深入 MFC 內部架構:Hook 機制 如何攔截視窗建立、訊息響應函數安裝 如何從 SDK 風格演進為訊息映射表、Subclass 子類化 機制、核心類別關係圖,以及 責任鏈模式 實現的命令路由系統。
Windows Hook 以「訂閱-發布」模式運作:系統維護各類 Hook 鏈表,事件發生時依序呼叫掛鉤函數。MFC 大量依賴 WH_CBT (Computer-Based Training Hook) 攔截 HCBT_CREATEWND 視窗建立事件。
// 簡化流程
SetWindowsHookEx(WH_CBT, AfxCbtFilterHook, ...);
// AfxCbtFilterHook 接收 HCBT_CREATEWND
// → 取得 CWnd* 對應關係
// → 修改 WNDCLASS.lpfnWndProc 為 AfxWndProc
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc; // 單一巨大 switch-case
RegisterClass(&wc);
hWnd = CreateWindow(wc.lpszClassName, ...);
CreateWindowEx,系統依 WNDCLASS.lpfnWndProc (預設 DefWindowProc) 建立視窗WH_CBT Hook 攔截 HCBT_CREATEWNDAfxCbtFilterHook 執行:
WNDCLASS.lpfnWndProc → CWnd::m_pfnSuperSetWindowLong(hWnd, GWL_WNDPROC, AfxWndProc) 替換視窗程序::AfxWndProc:
CWnd* (透過 CHandleMap)pWnd->OnWndMsg(msg, wp, lp, pResult)OnWndMsg 遍歷 訊息映射表,找到對應處理函數並調用CWnd::DefWindowProc → 內部調用 m_pfnSuper (原 DefWindowProc)class CWnd {
WNDPROC m_pfnSuper; // 原始視窗程序指標
HWND m_hWnd; // 視窗句柄
virtual BOOL OnWndMsg(UINT msg, WPARAM wp, LPARAM lp, LRESULT* pResult); // 訊息映射分派
virtual LRESULT DefWindowProc(UINT msg, WPARAM wp, LPARAM lp); // 預設處理
};
多態體現:OnWndMsg、DefWindowProc 均為虛函數,衍生類可覆寫。
邏輯與「訊息安裝」同構:
WNDPROCSubclassWindow(hWnd) → 內部同樣備份原 WNDPROC 到 m_pfnSuper、設定新 WNDPROCCWinApp (應用程式單例)
│
├─ CFrameWnd (框架視窗) ── CView (文檔視圖)
│ │
│ ├─ CMDIFrameWnd (MDI 主框架) ── CMDIChildWnd (MDI 子視窗)
│ │
│ └─ CDocument (文檔資料)
│
└─ CDocTemplate (文檔模板) ── 管理 Doc/View/Frame 建立
這些類別構成 命令路由候選者,共同實現責任鏈。
OnCmdMsg 向上查找基類訊息映射表 — 實現「繼承覆寫」語意,與路由無關CWinApp → CFrameWnd → CView → CDocument — 真正的責任鏈路由// 簡化版 CWinApp::OnCmdMsg
BOOL CWinApp::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pInfo) {
// 1. 嘗試自身處理
if (CWnd::OnCmdMsg(...)) return TRUE;
// 2. 轉發給主框架
if (m_pMainWnd && m_pMainWnd->OnCmdMsg(...)) return TRUE;
return FALSE;
}
// CFrameWnd::OnCmdMsg
BOOL CFrameWnd::OnCmdMsg(...) {
// 1. 嘗試自身
if (CWnd::OnCmdMsg(...)) return TRUE;
// 2. 轉發給作用中 View
if (m_pViewActive && m_pViewActive->OnCmdMsg(...)) return TRUE;
// 3. MDI: 轉發給作用中 Child Frame
return FALSE;
}
// CView::OnCmdMsg
BOOL CView::OnCmdMsg(...) {
// 1. 嘗試自身
if (CWnd::OnCmdMsg(...)) return TRUE;
// 2. 轉發給 Document
if (m_pDocument && m_pDocument->OnCmdMsg(...)) return TRUE;
return FALSE;
}
CWinApp
│
CMainFrame (CFrameWnd)
│
┌──────┴──────┐
│ │
CView1 CView2 (作用中)
│ │
CDoc1 CDoc2
CWinApp 起始,向下傳遞至框架、視圖、文檔| 模式 | MFC 體現位置 |
|---|---|
| Chain of Responsibility | 命令路由 (CWinApp → Frame → View → Document) |
| Composite | 視窗樹 (CWnd 父/子/兄弟/擁有者指標) |
| Observer / Publish-Subscribe | Windows Hook 機制 (WH_CBT, WH_GETMESSAGE...) |
| Template Method | CWnd::OnWndMsg / OnCmdMsg 定義演算法骨架,衍生類填充具體處理 |
| Bridge | CDC (邏輯) ↔ 驅動程式 (實體) 分離 |
MFC 並非單純「API 封裝」,而是在 Win32 API 基礎上,引入 Hook 機制實現視窗程序攔截、訊息映射表替代巨大 switch-case、責任鏈模式解耦命令處理者 的應用程式框架。理解這些底層機制,能讓開發者在遇到疑難雜症 (如訊息不觸發、路由中斷、Hook 衝突) 時,從架構層面快速定位根因,並為現代框架設計 (如 Qt、WPF、React) 提供歷史借鑑。