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

系統理解 Win32 API 與 MFC (下):MFC 概念模型、訊息路由、Hook 機制與責任鏈模式

     編輯:關於VC++

前言

承接上篇「Win32 API 領域模型」,本文深入 MFC 內部架構:Hook 機制 如何攔截視窗建立、訊息響應函數安裝 如何從 SDK 風格演進為訊息映射表、Subclass 子類化 機制、核心類別關係圖,以及 責任鏈模式 實現的命令路由系統。

1. Hook 機制:MFC 的隱形基石

Windows Hook 以「訂閱-發布」模式運作:系統維護各類 Hook 鏈表,事件發生時依序呼叫掛鉤函數。MFC 大量依賴 WH_CBT (Computer-Based Training Hook) 攔截 HCBT_CREATEWND 視窗建立事件。

// 簡化流程
SetWindowsHookEx(WH_CBT, AfxCbtFilterHook, ...);
// AfxCbtFilterHook 接收 HCBT_CREATEWND
//   → 取得 CWnd* 對應關係
//   → 修改 WNDCLASS.lpfnWndProc 為 AfxWndProc

2. MFC 訊息響應函數安裝:從 SDK 到訊息映射

2.1 SDK 風格 (回顧)

WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc; // 單一巨大 switch-case
RegisterClass(&wc);
hWnd = CreateWindow(wc.lpszClassName, ...);

2.2 MFC 風格:CWnd::CreateEx 內部流程

  1. 呼叫 CreateWindowEx,系統依 WNDCLASS.lpfnWndProc (預設 DefWindowProc) 建立視窗
  2. WH_CBT Hook 攔截 HCBT_CREATEWND
  3. Hook 函數 AfxCbtFilterHook 執行:
    • 備份原 WNDCLASS.lpfnWndProc → CWnd::m_pfnSuper
    • SetWindowLong(hWnd, GWL_WNDPROC, AfxWndProc) 替換視窗程序
  4. 後續所有訊息先進入 ::AfxWndProc:
    • 查找 CWnd* (透過 CHandleMap)
    • 呼叫 pWnd->OnWndMsg(msg, wp, lp, pResult)
    • OnWndMsg 遍歷 訊息映射表,找到對應處理函數並調用
    • 若未處理 → 呼叫 CWnd::DefWindowProc → 內部調用 m_pfnSuper (原 DefWindowProc)

2.3 關鍵結構體現

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 均為虛函數,衍生類可覆寫。

3. Subclass 子類化機制

邏輯與「訊息安裝」同構:

  • 既有視窗已有 WNDPROC
  • 呼叫 SubclassWindow(hWnd) → 內部同樣備份原 WNDPROC 到 m_pfnSuper、設定新 WNDPROC
  • 實現「外掛式」行為擴展,不修改原視窗類別代碼

4. 框架核心類別關係圖

CWinApp (應用程式單例)
   │
   ├─ CFrameWnd (框架視窗) ── CView (文檔視圖)
   │         │
   │         ├─ CMDIFrameWnd (MDI 主框架) ── CMDIChildWnd (MDI 子視窗)
   │         │
   │         └─ CDocument (文檔資料)
   │
   └─ CDocTemplate (文檔模板) ── 管理 Doc/View/Frame 建立

這些類別構成 命令路由候選者,共同實現責任鏈。

5. 責任鏈模式:命令路由系統

5.1 訊息傳遞雙向度

  • 縱向 (繼承鏈):OnCmdMsg 向上查找基類訊息映射表 — 實現「繼承覆寫」語意,與路由無關
  • 橫向 (物件鏈):CWinApp → CFrameWnd → CView → CDocument — 真正的責任鏈路由

5.2 路由流程

// 簡化版 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;
}

5.3 物件樹與路由方向

        CWinApp
           │
        CMainFrame (CFrameWnd)
           │
    ┌──────┴──────┐
    │             │
CView1         CView2 (作用中)
    │             │
CDoc1         CDoc2
  • 命令從 CWinApp 起始,向下傳遞至框架、視圖、文檔
  • 任一節點處理成功即停止傳遞
  • 符合 Chain of Responsibility 經典定義

6. 設計模式總結

模式MFC 體現位置
Chain of Responsibility命令路由 (CWinApp → Frame → View → Document)
Composite視窗樹 (CWnd 父/子/兄弟/擁有者指標)
Observer / Publish-SubscribeWindows Hook 機制 (WH_CBT, WH_GETMESSAGE...)
Template MethodCWnd::OnWndMsg / OnCmdMsg 定義演算法骨架,衍生類填充具體處理
BridgeCDC (邏輯) ↔ 驅動程式 (實體) 分離

總結

MFC 並非單純「API 封裝」,而是在 Win32 API 基礎上,引入 Hook 機制實現視窗程序攔截、訊息映射表替代巨大 switch-case、責任鏈模式解耦命令處理者 的應用程式框架。理解這些底層機制,能讓開發者在遇到疑難雜症 (如訊息不觸發、路由中斷、Hook 衝突) 時,從架構層面快速定位根因,並為現代框架設計 (如 Qt、WPF、React) 提供歷史借鑑。

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