滑鼠/鍵盤驅動程式 → 系統訊息佇列 → Windows 處理 → 執行緒訊息佇列 → 執行緒取出 → 發送至視窗程序
典型佇列訊息:WM_MOUSEMOVE、WM_CHAR、WM_PAINT、WM_TIMER、WM_QUIT
同一視窗的多個 WM_PAINT 合併為一個,合併所有無效區域。目的:減少重繪次數。系統總在佇列末尾加入訊息,保證先進先出。
繞過系統/執行緒佇列,直接發送至視窗程序。來源:
WM_ACTIVATE、WM_SETFOCUS、WM_SETCURSORSetWindowPos → WM_WINDOWPOSCHANGED| 方式 | 代表函式 | 行為特徵 |
|---|---|---|
| 發送 | SendMessage, SendMessageCallback, SendNotifyMessage, SendMessageTimeout | 同步,等待處理完成才回傳 |
| 佇列 | PostMessage, PostThreadMessage, PostQuitMessage | 非同步,放入佇列即回傳 |
| 廣播 | BroadcastSystemMessage, BroadcastSystemMessageEx | 發送給多類型接收者 |
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
hWnd 的執行緒佇列,不等處理即回傳hWnd == HWND_BROADCAST:發送給所有重疊/彈出視窗 (子視窗不收)hWnd == NULL:類似 PostThreadMessage (當前執行緒)發送:立即處理,處理完才回傳。佇列:放入 FIFO 佇列,空閒時處理,即時回傳。實際上發送訊息與直接調用視窗程序差異僅在:系統可攔截所有發送訊息,但無法攔截直接調用。
| 函式 | 特性 |
|---|---|
| GetMessage | 阻塞等待訊息,可依視窗/訊息範圍過濾,取得後從佇列移除 (WM_PAINT 除外) |
| PeekMessage | 非阻塞檢查佇列,有訊息才取出,可指定是否移除 (PM_REMOVE/PM_NOREMOVE) |
| WaitMessage | 無事可做時讓出 CPU,掛起至新訊息到達 |
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg); // WM_KEYDOWN/UP → WM_CHAR
DispatchMessage(&msg); // 發送至視窗程序
}
}
GetMessage 從主執行緒佇列取訊息,無訊息則等待WM_KEYFIRST/LAST (鍵盤)、WM_MOUSEFIRST/LAST (滑鼠)TranslateAccelerator:加速鍵處理TranslateMessage:雙鍵訊息合成字元訊息 (原鍵訊息仍會傳遞)DispatchMessage:發送至視窗程序WM_QUIT 導致 GetMessage 回傳 0 結束迴路PostQuitMessage 結束訊息迴路,通常在 WM_DESTROY 中呼叫if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
{
if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
// 處理 ESC 鍵
}
處理所有發送至該視窗訊息的函式。同類別視窗共用同一視窗程序。系統傳遞訊息資料作為參數,依訊息類型處理,參數區分細節。未處理訊息呼叫 DefWindowProc 執行預設處理。大多數視窗只處理少部分訊息。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
// 繪圖邏輯
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
將巨大 switch 拆解為小函式,每個訊息對應一個處理函式,便於管理。
void MsgCracker(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify)
{
switch (id)
{
case ID_A: if (codeNotify == EN_CHANGE) ... break;
case ID_B: if (codeNotify == BN_CLICKED) ... break;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HANDLE_MSG(hWnd, WM_COMMAND, MsgCracker);
HANDLE_MSG(hWnd, WM_DESTROY, MsgCracker);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
#define HANDLE_MSG(hwnd, msg, fn) \
case msg: return HANDLE_##msg((hwnd), (wParam), (lParam), (fn))
#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) \
((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam))), 0L
#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \
(void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id), (UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
HANDLE_MSG:將訊息分發至特定處理函式HANDLE_WM_XXXX:解析 wParam/lParam 傳遞給處理函式FORWARD_WM_XXXX:重構參數發送訊息 (模擬發送端行為)下回將分析訊息在 MFC 中的處理機制:訊息映射、CCmdTarget、命令路由等核心架構。