Windows 程式採用事件驅動模型。所有輸入(滑鼠、鍵盤、計時器、繪圖、系統事件)均封裝為訊息,投遞至目標視窗的視窗程序處理。MFC 在此基礎上建立訊息映射機制,以極高效率實現 C++ 多型般的動態分派。
| 分類維度 | 類別 | 範例 | 特徵 |
|---|---|---|---|
| 投遞路徑 | 佇列訊息 | WM_MOUSEMOVE, WM_KEYDOWN, WM_PAINT, WM_TIMER, WM_QUIT | 經系統佇列 → 執行緒佇列 → GetMessage/DispatchMessage |
| 非佇列訊息 | WM_ACTIVATE, WM_SETFOCUS, WM_WINDOWPOSCHANGED 等大多數 | 直接呼叫視窗程序,繞過佇列 | |
| 來源 | 系統訊息 (0 ~ WM_USER-1, 0x8000~0xBFFF) | WM_CREATE, WM_DESTROY, WM_SIZE | Windows 保留,語意固定 |
| 應用程式訊息 (WM_USER~0x7FFF) | WM_USER+100 | 單一應用程式內部自定義 | |
| 全域唯一訊息 (0xC000~0xFFFF) | RegisterWindowMessage("MyMsg") | 跨進程唯一,需註冊 |
GetMessage → TranslateMessage → DispatchMessage。DispatchMessage 呼叫視窗程序 (WndProc) 處理。關鍵點:WM_PAINT、WM_TIMER、WM_QUIT 是低優先級佇列訊息,佇列空時才處理(合併重繪、合併計時器)。
CWnd 需處理 100+ 訊息 → 虛擬函數表極大。// 靜態映射表項目
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // 訊息 ID (WM_XXX)
UINT nCode; // 控制項通知代碼 (BN_CLICKED 等) 或 0
UINT nID; // 控制項 ID 或 0
UINT nLastID; // 範圍結束 ID
AFX_PMSG pfn; // 處理函數指標 (成員函數)
};
// 類別靜態映射表
struct AFX_MSGMAP
{
const AFX_MSGMAP* pBaseMap; // 基類映射表
const AFX_MSGMAP_ENTRY* lpEntries; // 本類別項目陣列
};
BEGIN_MESSAGE_MAP(CMyWnd, CWnd)
ON_WM_PAINT()
ON_COMMAND(ID_BUTTON_OK, OnBtnOk)
ON_BN_CLICKED(IDC_CHECK, OnCheckClicked)
END_MESSAGE_MAP()
// 展開後產生靜態陣列:
const AFX_MSGMAP_ENTRY CMyWnd::_messageEntries[] = {
{ WM_PAINT, 0, 0, 0, (AFX_PMSG)&CMyWnd::OnPaint },
{ WM_COMMAND, 0, ID_BUTTON_OK, 0, (AFX_PMSG)&CMyWnd::OnBtnOk },
{ WM_COMMAND, BN_CLICKED, IDC_CHECK, 0, (AFX_PMSG)&CMyWnd::OnCheckClicked },
{ 0, 0, 0, 0, 0 } // 結束標記
};
const AFX_MSGMAP CMyWnd::messageMap = { &CWnd::messageMap, _messageEntries };
AfxWndProc (MFC 全域視窗程序) 接收訊息。CHandleMap 找到 CWnd* (或建立臨時 CTempWnd)。CWnd::WindowProc → OnWndMsg。OnWndMsg 遍歷 messageMap 及基類鏈表,比對 nMessage/nCode/nID。AFX_PMSG 強制轉型)。DefWindowProc 預設處理。效率:O(映射項目數),通常 < 20 次比較,極快。無虛擬函數表開銷。
命令 (WM_COMMAND, WM_SYSCOMMAND) 採用路由機制,依序嘗試:
CView)CDocument)CFrameWnd)CWinApp)CDocTemplate)實作於 CCmdTarget::OnCmdMsg。這使「另存新檔」等命令可在視圖、文檔、應用程式任一層級處理。
| 主題 | 關鍵點 |
|---|---|
| 訊息映射範圍宏 | ON_COMMAND_RANGE(id1, id2, fn)、ON_NOTIFY_RANGE 處理連續 ID。 |
| 動態訊息映射 | 罕用。可執行期修改映射表 (需鎖定),支援外掛架構。 |
| 反射訊息 | ON_WM_CTLCOLOR_REFLECT() 等,控制項自行處理通知,父視窗不介入。 |
| 訊息攔截 | 覆寫 PreTranslateMessage (對話框/視圖) 或 HookWindowCreate。 |
| 使用者訊息 | #define WM_MYMSG (WM_USER+100),配合 ON_MESSAGE(WM_MYMSG, OnMyMsg)。 |
| 註冊訊息 | UINT WM_FIND = RegisterWindowMessage(_T("MyFindMsg"));,跨進程通訊。 |
TRACE 宏在 OnWndMsg 印出未處理訊息。AfxWndProc 或 CWnd::WindowProc 觀察分派。messageMap 靜態陣列是否正確生成 (ClassWizard 同步問題)。MFC 訊息映射以靜態表格 + 基類鏈表遍歷取代虛擬函數,在記憶體效率、執行速度、維護便利性三方面達到平衡。理解其宏展開本質、分派流程、命令路由、反射機制,能讓您精準控制訊息流向,解決「訊息不觸發、重複處理、跨模組通訊」等疑難雜症,並在需要時突破框架限制實現自定義分派邏輯。