這是一個在 Windows 程式設計論壇中頻繁出現的經典問題。要解決它,首先必須理解 Windows 處理使用者輸入的機制與 DOS 時代截然不同。在 DOS 中,應用程式主動向作業系統請求輸入;而在 Windows 的事件驅動模型裡,當使用者產生按鍵或滑鼠事件時,是由作業系統主動呼叫應用程式的對應處理函數,處理完成後再將控制權交還給系統。
當使用者按下按鍵或移動滑鼠,系統會將這些事件封裝成訊息,存放在該應用程式的訊息佇列中。訊息會一直留在佇列裡,直到應用程式取出處理並將控制權歸還 Windows,系統才會發送下一條訊息。因此,若要在程式忙碌執行長時間運算時,仍能偵測使用者是否按下特定按鍵(如 Escape 鍵以取消操作),就必須主動檢查訊息佇列中是否存在該按鍵訊息。
這可以透過 PeekMessage 函數達成。以下範例示範如何在迴圈中檢查 Escape 鍵:
MSG msg;
// 檢查訊息佇列中是否有鍵盤訊息
if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) {
// 若是按下 Escape 鍵
if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) {
// 跳出迴圈、終止處理或執行取消邏輯
}
}
MSG 結構,用於接收訊息相關資訊。m_hWnd。WM_KEYFIRST 與 WM_KEYLAST 以篩選所有鍵盤訊息。PM_REMOVE 表示取出訊息後從佇列移除;PM_NOREMOVE 則保留在佇列。PeekMessage 若在指定範圍內找到訊息會傳回非零值。程式接著判斷是否為 WM_KEYDOWN 且 wParam 等於 VK_ESCAPE,若符合即可中斷長時間運作的迴圈,實現使用者取消操作的需求。
這項技巧在處理大量資料計算、檔案批次處理等耗時操作時極為實用,能讓介面保持回應,避免使用者以為程式當機而強制結束。