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

Win32 訊息迴圈進化論:從最基礎到 WTL 級工業標準實作

     編輯:組合語言

訊息迴圈是 Windows GUI 程式的心臟。看似簡單的 GetMessage → TranslateMessage → DispatchMessage,實則暗藏健壯性陷阱、快速鍵處理、對話方塊導航、ActiveX 整合與閒置處理等進階需求。本文分五個版本層層遞進,最終呈現 WTL CMessageLoop 的工業級實作細節。

版本一:教科書標準寫法(有隱患)


MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

缺陷:GetMessage 失敗時回傳 -1,但迴圈條件視為 TRUE,導致錯誤訊息被重複處理。

版本二:正確處理回傳值


BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
    if (bRet == -1) { /* 錯誤處理/退出 */ }
    else {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

版本三:引入 PreTranslateMessage(框架雛形)


BOOL PreTranslateMessage(LPMSG pMsg) { return FALSE; }

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
    if (bRet == -1) continue;
    if (!PreTranslateMessage(&msg)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

PreTranslateMessage 三大職責:

  1. 全域熱鍵/快速鍵(TranslateAccelerator)
  2. 第三方/通用控件訊息預處理
  3. 執行緒訊息處理

版本四:快速鍵與 ActiveX 整合


HACCEL hAcc = LoadAccelerator(hInst, MAKEINTRESOURCE(IDA_XXX));
BOOL PreTranslateMessage(LPMSG pMsg) {
    if (TranslateAccelerator(hWnd, hAcc, pMsg)) return TRUE;
    // ActiveX (如 WebBrowser) 必須轉發快速鍵
    IOleInPlaceActiveObject *pObj;
    if (SUCCEEDED(m_pBrowser->QueryInterface(IID_IOleInPlaceActiveObject, &pObj)) &&
        S_OK == pObj->TranslateAccelerator(pMsg)) {
        pObj->Release(); return TRUE;
    }
    return FALSE;
}

未轉發會導致 Tab 導航、Ctrl+C/V 等在內嵌瀏覽器失效。

版本五:對話方塊鍵盤導航


BOOL PreTranslateMessage(LPMSG pMsg) {
    if (IsDialogMessage(hDlg, pMsg)) return TRUE; // 處理 Tab/Enter/ESC 等
    return FALSE;
}
  • IsDialogMessage 非單純查詢,會主動處理焦點切換、預設按鈕觸發等。
  • 適用於非模態對話方塊;模態對話方塊自帶訊息迴圈無需處理。
  • 普通視窗若需控件鍵盤導航亦可呼叫。

終極形態:WTL CMessageLoop 核心實作

WTL 提供可擴充的訊息迴圈類別,核心 Run() 邏輯:

  • 閒置處理:PeekMessage(PM_NOREMOVE) 檢查佇列空時觸發 OnIdle,支援多 CIdleHandler。
  • 錯誤/退出處理:GetMessage 回傳 -1 記錄追蹤並 continue;0 則 break 退出。
  • 預轉發鏈:反向遍歷 m_aMsgFilter 陣列,任一 PreTranslateMessage 回傳 TRUE 即攔截。
  • 閒置訊息判定:IsIdleMessage 過濾 WM_MOUSEMOVE, WM_PAINT, WM_SYSTIMER 等不應觸發閒置的訊息。
  • 虛擬擴充點:PreTranslateMessage、OnIdle 皆可覆寫,支援複雜 UI 框架需求。

// 關鍵片段
for(;;) {
    while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE)) {
        if(!OnIdle(nIdleCount++)) bDoIdle = FALSE;
    }
    bRet = ::GetMessage(&m_msg, NULL, 0, 0);
    if(bRet == -1) { ATLTRACE(...); continue; }
    if(!bRet) break;
    if(!PreTranslateMessage(&m_msg)) {
        ::TranslateMessage(&m_msg);
        ::DispatchMessage(&m_msg);
    }
    if(IsIdleMessage(&m_msg)) { bDoIdle = TRUE; nIdleCount = 0; }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved