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

判斷應用程式是否停止回應:SendMessageTimeout 與未公開 API 實作指南

     編輯:關於C++

工作管理員的「未回應」標籤是如何判定的?核心原理極其簡單:向視窗發送 WM_NULL 訊息,若超時未處理即視為停止回應。本文完整實作 SendMessageTimeout 方案,並補充未公開的 IsHungAppWindow/IsHungThread 進階用法。

標準做法:SendMessageTimeout + WM_NULL


#include 

BOOL IsWindowResponding(HWND hWnd, DWORD timeoutMs = 5000) {
    // WM_NULL (0x0000) 什麼都不做,僅測試訊息佇列是否處理
    DWORD_PTR result;
    LONG_PTR ret = SendMessageTimeout(
        hWnd,           // 目標視窗
        WM_NULL,        // 訊息類型
        0, 0,           // wParam, lParam
        SMTO_ABORTIFHUNG | SMTO_BLOCK,  // 旗標:遇掛起中止、同步等待
        timeoutMs,      // 超時毫秒
        &result         // 回傳值(WM_NULL 無意義)
    );
    return ret != 0;    // 非 0 表示視窗程序在超時前處理了訊息
}

旗標詳解

旗標效果
SMTO_ABORTIFHUNG若目標視窗屬於掛起執行緒,立即返回 0,不等待超時。
SMTO_BLOCK阻塞呼叫執行緒直到超時或處理完成(預設行為)。
SMTO_NORMAL若目標掛起則繼續等待(不中止)。
SMTO_NOTIMEOUTIFNOTHUNG目標未掛起時無視超時,持續等待。

進階:未公開 API(Windows 內部使用)

位於 user32.dll,無標頭檔/匯入庫,需 GetProcAddress 動態載入。

IsHungAppWindow (NT 系列)


typedef BOOL (WINAPI *PFN_IsHungAppWindow)(HWND);
PFN_IsHungAppWindow pfnIsHungAppWindow = 
    (PFN_IsHungAppWindow)GetProcAddress(GetModuleHandle("user32.dll"), "IsHungAppWindow");

if (pfnIsHungAppWindow && pfnIsHungAppWindow(hWnd)) {
    // 視窗屬於停止回應的應用程式
}

IsHungThread (9x 系列)


typedef BOOL (WINAPI *PFN_IsHungThread)(DWORD);
PFN_IsHungThread pfnIsHungThread = 
    (PFN_IsHungThread)GetProcAddress(GetModuleHandle("user32.dll"), "IsHungThread");

DWORD tid = GetWindowThreadProcessId(hWnd, NULL);
if (pfnIsHungThread && pfnIsHungThread(tid)) {
    // 視窗執行緒停止回應
}

完整範例:列舉所有視窗並標記狀態


BOOL CALLBACK EnumProc(HWND hWnd, LPARAM lParam) {
    if (!IsWindowVisible(hWnd)) return TRUE;
    char title[256];
    GetWindowText(hWnd, title, 256);
    if (title[0] == 0) return TRUE;

    BOOL responding = IsWindowResponding(hWnd, 2000);
    printf("%s: %s\n", title, responding ? "正常" : "未回應");
    return TRUE;
}

int main() {
    EnumWindows(EnumProc, 0);
    return 0;
}

適用場景與限制

  • 僅限 GUI 執行緒:主執行緒跑緊迴圈未泵訊息時會被判定為掛起;純背景執行緒無視窗則無法檢測。
  • 誤判風險:長時間運算(>5 秒)若未 PeekMessage 會被誤判;解決方案:長運算拆到背景執行緒或定期 MsgWaitForMultipleObjects。
  • 權限:跨完整性等級(Low→High)需 ChangeWindowMessageFilterEx 允許 WM_NULL。

最佳實務

  1. 優先使用 SendMessageTimeout(WM_NULL, SMTO_ABORTIFHUNG, 5000),標準、有文件、跨版本穩定。
  2. 僅在需要極低延遲檢測(如即時監控)時嘗試 IsHungAppWindow,並做好動態載入失敗的降級處理。
  3. 自家應用程式:主執行緒只做 UI,耗時工作全丟背景執行緒,從根源避免「未回應」。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved