看門狗作為獨立進程監控目標應用,核心職責:進程存在性檢查、響應性檢測、超閾值重啟、系統級恢復。採用"分離關注點"設計,看門狗與被監控程序解耦。
BOOL IsProcessRunning(DWORD pid) {
HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (!hProcess) return FALSE;
DWORD result = WaitForSingleObject(hProcess, 0);
CloseHandle(hProcess);
return result == WAIT_TIMEOUT; // 仍在運行
}
// 動態導入未文檔API
typedef BOOL (WINAPI *PFN_IsHungAppWindow)(HWND);
typedef BOOL (WINAPI *PFN_IsHungThread)(DWORD);
PFN_IsHungAppWindow pIsHungAppWindow = nullptr;
PFN_IsHungThread pIsHungThread = nullptr;
void InitHungDetection() {
HMODULE hUser32 = GetModuleHandleW(L"USER32.DLL");
pIsHungAppWindow = (PFN_IsHungAppWindow)GetProcAddress(hUser32, "IsHungAppWindow");
pIsHungThread = (PFN_IsHungThread)GetProcAddress(hUser32, "IsHungThread");
}
BOOL IsWindowHung(HWND hwnd) {
if (pIsHungAppWindow) return pIsHungAppWindow(hwnd);
return FALSE;
}
BOOL IsThreadHung(DWORD threadId) {
if (pIsHungThread) return pIsHungThread(threadId);
return FALSE;
}
struct WatchdogConfig {
std::wstring targetExePath;
DWORD checkIntervalMs = 5000; // 檢查間隔
DWORD maxHungCount = 3; // 最大無響應容忍次數
BOOL rebootOnExceed = FALSE; // 超限是否重啟系統
};
class Watchdog {
WatchdogConfig config;
DWORD hungCount = 0;
DWORD targetPid = 0;
HANDLE hTargetProcess = nullptr;
public:
void Run() {
while (true) {
Sleep(config.checkIntervalMs);
if (!EnsureTargetRunning()) continue;
if (CheckHung()) {
if (++hungCount >= config.maxHungCount) {
if (config.rebootOnExceed) InitiateSystemReboot();
else RestartTarget();
hungCount = 0;
}
} else {
hungCount = 0; // 重置計數器
}
}
}
private:
BOOL EnsureTargetRunning() {
if (targetPid && IsProcessRunning(targetPid)) return TRUE;
// 啟動新進程
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessW(config.targetExePath.c_str(), NULL, NULL, NULL, FALSE,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
targetPid = pi.dwProcessId;
hTargetProcess = pi.hProcess;
CloseHandle(pi.hThread);
return TRUE;
}
return FALSE;
}
BOOL CheckHung() {
// 遍歷目標進程所有可見窗口
EnumThreadWindows(targetPid, [](HWND hwnd, LPARAM lParam) -> BOOL {
Watchdog* self = (Watchdog*)lParam;
if (IsWindowVisible(hwnd) && IsWindowHung(hwnd)) {
*(BOOL*)lParam = TRUE; // 發現無響應窗口
return FALSE; // 停止枚舉
}
return TRUE;
}, (LPARAM)&hungDetected);
return hungDetected;
}
void RestartTarget() {
if (hTargetProcess) {
TerminateProcess(hTargetProcess, 0xDEAD);
WaitForSingleObject(hTargetProcess, 5000);
CloseHandle(hTargetProcess);
hTargetProcess = nullptr;
}
EnsureTargetRunning();
}
void InitiateSystemReboot() {
// 需SE_SHUTDOWN_NAME特權
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
TOKEN_PRIVILEGES tp = { 1, {{SE_SHUTDOWN_NAME, SE_PRIVILEGE_ENABLED}} };
AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
ExitWindowsEx(EWX_REBOOT | EWX_FORCE, SHTDN_REASON_MAJOR_SOFTWARE);
}
};
CREATE_NO_WINDOW、ShowWindow(hWnd, SW_HIDE)CreateMutex(NULL, FALSE, L"Global\\MyWatchdogMutex")防止多實例ReportEvent或ETW,便於事後分析