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

深度解析 MFC 訊息機制(下):訊息映射架構與實作原理

     編輯:關於VC++

MFC 訊息處理實現方式

初看 MFC 訊息,直覺會想到 C++ 虛擬函式機制。但經分析,MFC 採用「訊息映射」機制而非虛擬函式。原因在於:CWnd 約 110 個訊息,加上其他類別,若用虛擬函式表,每個視窗類型需 440KB vtable。更關鍵的是功能表命令、按鈕命令等應用程式專屬訊息,虛擬函式無法處理動態命令 ID。訊息映射避免巨大 vtable,同時支援標準視窗訊息與應用程式命令訊息的統一處理。

本質上,MFC 訊息機制是一張巨大的「訊息 ID ↔ 處理函式」對應表,加上分析處理此表的框架程式碼,避免 SDK 風格的冗長 switch-case。

訊息映射基類:CCmdTarget

欲支援訊息映射的類別必須繼承 CCmdTarget。此類別是 MFC 處理命令訊息的核心基礎,所有回應訊息/事件的類別皆繼承自它:應用程式類、框架類、文件類、視圖類、各類控制項類等。

核心成員函式

  • DispatchCmdMsg (static):MFC 內部使用,分派 Windows 訊息
  • OnCmdMsg (virtual):傳遞/發送命令、更新使用者介面物件狀態

OnCmdMsg 預設實現邏輯

  1. 呼叫虛擬函式 GetMessageMap 取得目前命令目標類別的訊息映射陣列 _messageEntries
  2. 在陣列中比對命令 ID 與控制項通知代碼
  3. 找到匹配項 → 用 DispatchCmdMsg 呼叫處理函式
  4. 未找到 → 用 GetBaseMessageMap 取得基類映射陣列,遞迴查找至 CCmdTarget
  5. 完全未找到 → 回傳 FALSE

命令路由機制

繼承類別可覆寫 OnCmdMsg 決定是否處理特定命令,無法處理時調用下一個命令目標的 OnCmdMsg。標準框架中,框架視窗類別覆寫 OnCmdMsg 實現標準命令路由路徑。應用程式可覆寫改變路由規則(如中斷順序轉給非預設物件)。

訊息映射內容結構

標頭檔 (.h) - 宣告巨集

class CMyClass : public CBaseClass
{
    // ...
    DECLARE_MESSAGE_MAP()
};

實作檔 (.cpp) - 映射表定義


BEGIN_MESSAGE_MAP(CMyClass, CBaseClass)
    //{{AFX_MSG_MAP(CMyClass)
    ON_WM_PAINT()
    ON_COMMAND(ID_MY_CMD, OnMyCommand)
    ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST, OnItemChanged)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

訊息處理運作的三大必要元素

  1. 類別定義中加入處理函式宣告 (在 //}}AFX_MSG 之後)
  2. 訊息映射表中加入映射項目 (在 //}}AFX_MSG_MAP 之後)
  3. 類別實作中加入處理函式定義

新增訊息處理的兩種方法

1. Class Wizard 自動新增 (Visual Studio 6.0/2003 風格)

  • View → Class Wizard 或右鍵選 Class Wizard
  • 選 Message Maps 標籤
  • Class name 選目標類別
  • Object IDs 選類別名稱
  • Messages 列表顯示可重載虛擬函式 (上方、大小寫) 與視窗訊息 (大寫)
  • 選中訊息點擊 Add Function 自動生成三大元素
  • 找不到訊息時:Class Info 標籤 → Message Filter 選 Window 顯示所有視窗訊息

2. 手動新增訊息處理

Class Wizard 列表無目標訊息時 (系統忽略或自訂訊息) 需手動處理:

  1. .h 檔://}}AFX_MSG 之後加入宣告,必須以 afx_msg 開頭,置於 Wizard 維護區塊 {{ }} 外部
  2. .cpp 檔://}}AFX_MSG_MAP 之後加入映射項目,同樣置於 {{ }} 外部
  3. .cpp 檔:加入處理函式實作

進階訊息映射巨集

巨集用途範例
ON_COMMAND功能表/按鈕/工具列命令ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
ON_COMMAND_RANGE連續命令 ID 範圍ON_COMMAND_RANGE(ID_COLOR_RED, ID_COLOR_BLUE, OnColorSelect)
ON_UPDATE_COMMAND_UI命令 UI 更新 (啟用/勾選)ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, OnUpdateViewToolbar)
ON_WM_XXX標準視窗訊息ON_WM_PAINT(), ON_WM_TIMER(), ON_WM_SIZE()
ON_NOTIFY通用控制項通知ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST, OnColumnClick)
ON_NOTIFY_RANGE控制項 ID 範圍通知ON_NOTIFY_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, OnToolTipText)
ON_CONTROL標準控制項通知ON_CONTROL(BN_CLICKED, IDC_BUTTON, OnButtonClick)
ON_REGISTERED_MESSAGE註冊訊息 (RegisterWindowMessage)ON_REGISTERED_MESSAGE(WM_MY_CUSTOM, OnCustomMsg)
ON_MESSAGE自訂訊息 (WM_USER+xxx)ON_MESSAGE(WM_USER+100, OnMyMessage)

命令路由標準路徑

框架視窗 → 檢視視圖 → 文件 → 文件樣板 → 應用程式物件

覆寫 OnCmdMsg 可插入自訂路由節點。

現代 MFC 開發建議

  • Visual Studio 2010+ 使用 屬性視窗/事件屬性 取代 Class Wizard
  • 右鍵類別檢視 → Add Event Handler 或屬性視窗閃電圖示
  • 訊息映射巨集支援 IntelliSense 自動完成
  • 大型專案建議將訊息映射表拆分多區段便於維護
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved