【问题标题】:Waiting for grandchild processes in windows在 Windows 中等待孙子进程
【发布时间】:2014-04-01 22:41:14
【问题描述】:

是否可以等待 Windows 中子进程启动的所有进程?我无法修改子进程或孙子进程。

具体来说,这就是我想要做的。我的进程启动uninstallA.exe。 uninitallA.exe进程启动uninstallB.exe并立即退出,uninstallB.exe运行一段时间。我想等待uninstallB.exe退出,以便知道卸载何时完成。

【问题讨论】:

    标签: winapi


    【解决方案1】:

    使用named mutex

    【讨论】:

    • 我觉得我做不到。我无法修改子进程。
    【解决方案2】:

    一种可能是安装 Cygwin,然后使用 ps 命令监视孙子退出

    【讨论】:

      【解决方案3】:

      没有一种通用的方法来等待所有的孙子,但对于您的具体情况,您可能可以一起破解一些东西。您知道您正在寻找一个特定的流程实例。我会首先等待uninstallA.exe 退出(使用WaitForSingleObject),因为此时您知道uninstallB.exe 已经启动。然后使用 PSAPI 中的 EnumProcesses 和 GetProcessImageFileName 来查找正在运行的 uninstallB.exe 实例。没有找到就知道已经完成了,否则可以等待。

      另外一个复杂的情况是,如果您需要支持早于 XP 的 Windows 版本,则不能使用 GetProcessImageFileName,而对于 Windows NT,则根本不能使用 PSAPI。对于 Windows 2000,您可以使用 GetModuleFileNameEx,但它有一些警告意味着它有时可能会失败(查看文档)。如果您必须支持 NT,请查看 Toolhelp32。

      是的,这太丑了。

      【讨论】:

      • 无论如何您都可以考虑使用 Toolhelp32,因为它提供了每个进程的父(生成)ID,并且 CreateProcess() 也返回了一个进程 ID。您应该能够启动uninstallA.exe,获取其进程ID,然后使用ToolHelp32 查找由同一进程ID 启动的所有进程。在您关闭 CreateProcess() 返回的进程句柄之前,操作系统无法重用 uninstallA 的进程 ID。
      【解决方案4】:

      使用CreateJobObject 创建一个作业对象。使用CreateProcess 以挂起状态启动UninstallA.exe。使用AssignProcessToJobObject 将该新流程分配给您的作业对象。通过在从CreateProcess 返回的线程的句柄上调用ResumeThread 来启动UninstallA.exe。

      然后是困难的部分:等待作业对象完成其执行。不幸的是,这比任何人合理希望的要复杂得多。基本思想是创建一个 I/O 完成端口,然后创建对象对象,将其与 I/O 完成端口关联,最后在 I/O 完成端口上等待(通过GetQueuedCompletionStatus 获取其状态) . Raymond Chen 在他的blog 上有一个演示(并解释了这是如何发生的)。

      【讨论】:

      【解决方案5】:

      这是一种技术,虽然不是绝对可靠,但如果由于某种原因您不能使用作业对象,它可能会很有用。想法是创建一个匿名管道,让子进程继承管道写入端的句柄。

      通常,孙进程也将继承管道的写入端。特别是,cmd.exe 启动的进程(例如,从批处理文件中)将继承句柄。

      一旦子进程退出,父进程将关闭其对管道写入端的句柄,然后尝试从管道读取。由于没有人在写入管道,因此读取操作将无限期地阻塞。 (当然,如果你想在等待孙辈的同时继续做事,你可以使用线程或异步 I/O。)

      当(且仅当)管道写入端的最后一个句柄关闭时,管道的写入端会自动销毁。这会中断管道,读取操作完成并报告 ERROR_BROKEN_PIPE 失败。

      多年来,我一直在生产环境中使用此代码(以及相同代码的早期版本)。

      // pwatch.c
      //
      // Written in 2011 by Harry Johnston, University of Waikato, New Zealand.
      // This code has been placed in the public domain.  It may be freely
      // used, modified, and distributed.  However it is provided with no
      // warranty, either express or implied.
      //
      // Launches a process with an inherited pipe handle,
      // and doesn't exit until (a) the process has exited 
      // and (b) all instances of the pipe handle have been closed.
      //
      // This effectively waits for any child processes to exit,
      // PROVIDED the child processes were created with handle
      // inheritance enabled.  This is usually but not always
      // true.
      //
      // In particular if you launch a command shell (cmd.exe)
      // any commands launched from that command shell will be
      // waited on.
      
      #include <windows.h>
      
      #include <stdio.h>
      
      void error(const wchar_t * message, DWORD err) {
      
        wchar_t msg[512];
      
        swprintf_s(msg, sizeof(msg)/sizeof(*msg), message, err);
      
        printf("pwatch: %ws\n", msg);
      
        MessageBox(NULL, msg, L"Error in pwatch utility", MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
      
        ExitProcess(err);
      
      }
      
      int main(int argc, char ** argv) {
      
        LPWSTR lpCmdLine = GetCommandLine();
      
        wchar_t ch;
      
        DWORD dw, returncode;
      
        HANDLE piperead, pipewrite;
      
        STARTUPINFO si;
      
        PROCESS_INFORMATION pi;
      
        SECURITY_ATTRIBUTES sa;
      
        char buffer[1];
      
        while (ch = *(lpCmdLine++)) {
      
          if (ch == '"') while (ch = *(lpCmdLine++)) if (ch == '"') break;
      
          if (ch == ' ') break;
      
        }
      
        while (*lpCmdLine == ' ') lpCmdLine++;
      
        sa.nLength = sizeof(sa);
        sa.bInheritHandle = TRUE;
        sa.lpSecurityDescriptor = NULL;
      
        if (!CreatePipe(&piperead, &pipewrite, &sa, 1)) error(L"Unable to create pipes: %u", GetLastError());
      
        GetStartupInfo(&si);
      
        if (!CreateProcess(NULL, lpCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) 
          error(L"Error %u creating process.", GetLastError());
      
        if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) error(L"Error %u waiting for process.", GetLastError());
      
        if (!GetExitCodeProcess(pi.hProcess, &returncode)) error(L"Error %u getting exit code.", GetLastError());
      
        CloseHandle(pipewrite);
      
        if (ReadFile(piperead, buffer, 1, &dw, NULL)) {
      
          error(L"Unexpected data received from pipe; bug in application being watched?", ERROR_INVALID_HANDLE);
      
        }
      
        dw = GetLastError();
      
        if (dw != ERROR_BROKEN_PIPE) error(L"Unexpected error %u reading from pipe.", dw);
      
        return returncode;
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-05
        • 1970-01-01
        • 2013-09-10
        • 2021-03-29
        • 2013-09-15
        • 1970-01-01
        相关资源
        最近更新 更多