You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下C++实现无递归双向进程守护(进程配对)的方法

解决Windows C++双向进程守护的递归启动问题

核心问题分析

你遇到的递归启动循环,本质是两个进程之间没有传递明确的目标守护PID,导致启动时无法识别已存在的对方进程,进而重复启动新实例。解决的关键是启动时传递目标进程PID,并验证该PID对应的进程确实是需要守护的目标程序。

解决方案步骤

1. 启动时传递目标PID

启动对方进程时,将自身PID作为命令行参数传递,让对方明确知道需要守护的进程ID。

2. 验证PID有效性(避免PID复用)

Windows会复用已终止进程的PID,因此不能仅通过IsProcessAlive判断,还需要验证该PID对应的进程是否是目标可执行文件(比如MainApp.exe或Watchdog.exe)。

3. 修正双向守护逻辑

  • 进程启动时先检查命令行参数中的目标PID是否有效且存活
  • 若无效则启动目标进程,并传递自身PID
  • 进入循环监控目标进程,异常时重启

代码实现示例

通用工具函数(可复用)

#include <windows.h>
#include <psapi.h>
#include <shlwapi.h>
#include <string>

#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "shlwapi.lib")

// 原有的进程存活检测函数
bool IsProcessAlive(DWORD pid) {
    HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
    if (!h) return false;

    DWORD exitCode;
    bool alive = GetExitCodeProcess(h, &exitCode) && exitCode == STILL_ACTIVE;

    CloseHandle(h);
    return alive;
}

// 验证PID对应的进程是否是目标exe
bool IsTargetProcess(DWORD pid, const std::wstring& targetExeName) {
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (!hProcess) return false;

    WCHAR exePath[MAX_PATH] = {0};
    GetModuleFileNameExW(hProcess, nullptr, exePath, MAX_PATH);
    std::wstring exeName = PathFindFileNameW(exePath);

    bool isMatch = _wcsicmp(exeName.c_str(), targetExeName.c_str()) == 0;
    CloseHandle(hProcess);
    return isMatch;
}

MainApp.exe 核心代码

// 启动Watchdog并传递自身PID
DWORD StartWatchdog() {
    DWORD selfPid = GetCurrentProcessId();
    std::wstring cmdLine = L"Watchdog.exe " + std::to_wstring(selfPid);

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = {0};

    if (CreateProcessW(L"Watchdog.exe", &cmdLine[0], nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
        CloseHandle(pi.hThread);
        DWORD watchdogPid = pi.dwProcessId;
        CloseHandle(pi.hProcess);
        return watchdogPid;
    }
    return 0;
}

int wmain(int argc, wchar_t* argv[]) {
    DWORD watchdogPid = 0;

    // 读取命令行传递的Watchdog PID
    if (argc >= 2) {
        watchdogPid = _wtoi(argv[1]);
    }

    // 验证Watchdog是否有效存活
    if (watchdogPid == 0 || !IsProcessAlive(watchdogPid) || !IsTargetProcess(watchdogPid, L"Watchdog.exe")) {
        watchdogPid = StartWatchdog();
    }

    // 进入守护循环
    while (true) {
        if (!IsProcessAlive(watchdogPid)) {
            watchdogPid = StartWatchdog();
        }
        Sleep(1000); // 每秒检查一次,可根据需求调整间隔
    }

    return 0;
}

Watchdog.exe 核心代码

// 启动MainApp并传递自身PID
DWORD StartMainApp() {
    DWORD selfPid = GetCurrentProcessId();
    std::wstring cmdLine = L"MainApp.exe " + std::to_wstring(selfPid);

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = {0};

    if (CreateProcessW(L"MainApp.exe", &cmdLine[0], nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
        CloseHandle(pi.hThread);
        DWORD mainAppPid = pi.dwProcessId;
        CloseHandle(pi.hProcess);
        return mainAppPid;
    }
    return 0;
}

int wmain(int argc, wchar_t* argv[]) {
    DWORD mainAppPid = 0;

    // 读取命令行传递的MainApp PID
    if (argc >= 2) {
        mainAppPid = _wtoi(argv[1]);
    }

    // 验证MainApp是否有效存活
    if (mainAppPid == 0 || !IsProcessAlive(mainAppPid) || !IsTargetProcess(mainAppPid, L"MainApp.exe")) {
        mainAppPid = StartMainApp();
    }

    // 进入守护循环
    while (true) {
        if (!IsProcessAlive(mainAppPid)) {
            mainAppPid = StartMainApp();
        }
        Sleep(1000);
    }

    return 0;
}

关键注意事项

  • PID复用防护:必须通过进程名验证PID归属,避免Windows PID复用导致的误判。
  • 权限处理:确保程序拥有足够权限调用OpenProcess和GetModuleFileNameEx,必要时以管理员权限运行。
  • 异常与日志:建议添加错误日志记录(比如写入文件),方便排查启动失败或守护异常问题。
  • 互斥量(可选):如果需要防止目标进程多实例运行,可创建全局互斥量(如CreateMutexW(nullptr, TRUE, L"Global\\MainAppUniqueMutex")),启动时先检查互斥量是否存在。

内容的提问来源于stack exchange,提问作者Макс Холодов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 09:50:54