初看 MFC 訊息,直覺會想到 C++ 虛擬函式機制。但經分析,MFC 採用「訊息映射」機制而非虛擬函式。原因在於:CWnd 約 110 個訊息,加上其他類別,若用虛擬函式表,每個視窗類型需 440KB vtable。更關鍵的是功能表命令、按鈕命令等應用程式專屬訊息,虛擬函式無法處理動態命令 ID。訊息映射避免巨大 vtable,同時支援標準視窗訊息與應用程式命令訊息的統一處理。
本質上,MFC 訊息機制是一張巨大的「訊息 ID ↔ 處理函式」對應表,加上分析處理此表的框架程式碼,避免 SDK 風格的冗長 switch-case。
欲支援訊息映射的類別必須繼承 CCmdTarget。此類別是 MFC 處理命令訊息的核心基礎,所有回應訊息/事件的類別皆繼承自它:應用程式類、框架類、文件類、視圖類、各類控制項類等。
GetMessageMap 取得目前命令目標類別的訊息映射陣列 _messageEntriesDispatchCmdMsg 呼叫處理函式GetBaseMessageMap 取得基類映射陣列,遞迴查找至 CCmdTargetFALSE繼承類別可覆寫 OnCmdMsg 決定是否處理特定命令,無法處理時調用下一個命令目標的 OnCmdMsg。標準框架中,框架視窗類別覆寫 OnCmdMsg 實現標準命令路由路徑。應用程式可覆寫改變路由規則(如中斷順序轉給非預設物件)。
class CMyClass : public CBaseClass
{
// ...
DECLARE_MESSAGE_MAP()
};
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()
//}}AFX_MSG 之後)//}}AFX_MSG_MAP 之後)View → Class Wizard 或右鍵選 Class WizardMessage Maps 標籤Class name 選目標類別Object IDs 選類別名稱Messages 列表顯示可重載虛擬函式 (上方、大小寫) 與視窗訊息 (大寫)Add Function 自動生成三大元素Class Info 標籤 → Message Filter 選 Window 顯示所有視窗訊息Class Wizard 列表無目標訊息時 (系統忽略或自訂訊息) 需手動處理:
//}}AFX_MSG 之後加入宣告,必須以 afx_msg 開頭,置於 Wizard 維護區塊 {{ }} 外部//}}AFX_MSG_MAP 之後加入映射項目,同樣置於 {{ }} 外部| 巨集 | 用途 | 範例 |
|---|---|---|
| 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 可插入自訂路由節點。
Add Event Handler 或屬性視窗閃電圖示