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

深度解析 VC 訊息機制(中):佇列/非佇列訊息、發送與佇列、訊息分發器

     編輯:關於VC++

佇列訊息 vs 非佇列訊息

訊息佇列架構

  • 系統訊息佇列:Windows 維護
  • 執行緒訊息佇列:每個 GUI 執行緒自行維護
  • 非 GUI 執行緒建立時無佇列,首次呼叫 GDI 函式時系統建立

佇列訊息流向

滑鼠/鍵盤驅動程式 → 系統訊息佇列 → Windows 處理 → 執行緒訊息佇列 → 執行緒取出 → 發送至視窗程序

典型佇列訊息:WM_MOUSEMOVE、WM_CHAR、WM_PAINT、WM_TIMER、WM_QUIT

特殊合併機制

同一視窗的多個 WM_PAINT 合併為一個,合併所有無效區域。目的:減少重繪次數。系統總在佇列末尾加入訊息,保證先進先出。

非佇列訊息

繞過系統/執行緒佇列,直接發送至視窗程序。來源:

  • 系統通知視窗狀態變化:WM_ACTIVATE、WM_SETFOCUS、WM_SETCURSOR
  • 應用程式呼叫系統函式產生:SetWindowPos → WM_WINDOWPOSCHANGED

訊息發送三大方式

方式代表函式行為特徵
發送SendMessage, SendMessageCallback, SendNotifyMessage, SendMessageTimeout同步,等待處理完成才回傳
佇列PostMessage, PostThreadMessage, PostQuitMessage非同步,放入佇列即回傳
廣播BroadcastSystemMessage, BroadcastSystemMessageEx發送給多類型接收者

SendMessage 詳細行為

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
  • 同進程視窗:視窗程序作為子程式立即被調用
  • 跨執行緒視窗:系統切換至目標執行緒調用視窗程序,不進入目標佇列
  • 回傳值由接收視窗程序決定

PostMessage 詳細行為

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 中呼叫

PeekMessage 實用範例:偵測 ESC 鍵

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;
}

WindowsX.h 核心巨集定義


#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、命令路由等核心架構。

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