如何在Windows上用C++实现互为看门狗的进程配对,并且不使用递归?

编程语言 2026-07-11

我正在用C++(使用MSVC)开发一个Windows控制台应用程序,并且我想实现一个互相看门狗系统。

目标:我有两个进程:MainApp.exeWatchdog.exe

  1. 如果 MainApp 被关闭或崩溃,Watchdog 应该重新启动它。
  2. 如果 Watchdog 被终止,MainApp 应该重新启动它。

问题:目前我遇到了一个“进程递归”的问题。当 MainApp 启动时,它会启动 Watchdog。之后 Watchdog 会检查 MainApp,如果它不能正确识别现有的父进程,它就会启动一个新的 MainApp 实例,而这又会启动一个新的 Watchdog,如此往复,直到耗尽系统资源。

bool ProcessGuard::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;
}

我本来想这样做,但我理解到当我的MainApp启动watchdog时,watchdog不知道mainApp的进程ID。

解决方案

实现可以如下所示(代码 请参考自)

void WaitForProcess(HANDLE hProcess, BOOL bMain);

void StartProcess(BOOL bMain)
{
    if (PWSTR lpApplicationName = new WCHAR[MINSHORT])
    {
        GetModuleFileNameW(0, lpApplicationName, MINSHORT);

        if (!GetLastError())
        {
            HANDLE hProcess;
            STARTUPINFOEXW si = { { sizeof(si) } };

            SIZE_T s = 0;
            ULONG dwError;
            while (ERROR_INSUFFICIENT_BUFFER == (dwError = InitializeProcThreadAttributeList(
                si.lpAttributeList, 1, 0, &s) ? NOERROR : GetLastError()))
            {
                si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)alloca(s);
            }

            if (NOERROR == dwError && UpdateProcThreadAttribute(si.lpAttributeList, 0, 
                PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &hProcess, sizeof(hProcess), 0, 0) &&
                DuplicateHandle(NtCurrentProcess(), NtCurrentProcess(), NtCurrentProcess(), 
                &hProcess, 0, TRUE, DUPLICATE_SAME_ACCESS))
            {
                PWSTR lpCommandLine = 0;
                int len = 0;

                while (0 < (len = _snwprintf(lpCommandLine, len, L"\n%p\r*%x*", hProcess, bMain)))
                {
                    if (lpCommandLine)
                    {
                        PROCESS_INFORMATION pi;
                        if (CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, TRUE, 
                            EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
                        {
                            NtClose(pi.hThread);
                            WaitForProcess(pi.hProcess, bMain);
                            NtClose(pi.hProcess);
                        }
                        break;
                    }

                    lpCommandLine = (PWSTR)alloca(++len * sizeof(WCHAR));
                }

                NtClose(hProcess);
            }
        }
        delete [] lpApplicationName;
    }
}

VOID
NTAPI
wcb (
     _Inout_     PTP_CALLBACK_INSTANCE /*Instance*/,
     _Inout_opt_ PVOID                 Context,
     _Inout_     PTP_WAIT              Wait,
     _In_        TP_WAIT_RESULT        /*WaitResult*/
     )
{
    TpReleaseWait(Wait);
    StartProcess((BOOL)(ULONG_PTR)Context);
}

void WaitForProcess(HANDLE hProcess, BOOL bMain)
{
    PTP_WAIT Wait;
    if (0 <= TpAllocWait(&Wait, wcb, (PVOID)(ULONG_PTR)bMain, 0))
    {
        TpSetWait(Wait, hProcess, 0);
    }
}

void WINAPI ep(void*)
{
    BOOL bMain = TRUE;
    if (PWSTR psz = wcschr(GetCommandLineW(), '*'))
    {
        ULONG b = wcstoul(psz + 1, &psz, 16);
        if ('*' == *psz)
        {
            bMain = b;
        }
    }

    if (PWSTR psz = wcschr(GetCommandLineW(), '\n'))
    {
        HANDLE hProcess = (HANDLE)(ULONG_PTR)_wcstoui64(psz + 1, &psz, 16);
        if ('\r' == *psz)
        {
            WaitForProcess(hProcess, !bMain);
            NtClose(hProcess);
        }
    }
    else
    {
        StartProcess(!bMain);
    }

    MessageBoxW(0, 0, bMain ? L"MainApp" : L"Watchdog", bMain ? MB_ICONINFORMATION : MB_ICONWARNING);
    ExitProcess(0);
}

这可以通过以下方式终止:

void Assign(HANDLE hJob, DWORD pid)
{
    if (HANDLE hProcess = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, FALSE, pid))
    {
        AssignProcessToJobObject(hJob, hProcess);
        CloseHandle(hProcess);
    }
}

void Kill2(DWORD pid1, DWORD pid2) 
{
    if (HANDLE hJob = CreateJobObjectW(0, 0))
    {
        Assign(hJob, pid1);
        Assign(hJob, pid2);
        TerminateJobObject(hJob, 0);
        CloseHandle(hJob);
    }   
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章