在Windows中等待孙子进程

时间:2009-11-05 21:50:17

标签: winapi

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

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

5 个答案:

答案 0 :(得分:9)

使用CreateJobObject创建工作对象。使用CreateProcess以暂停状态启动UninstallA.exe。使用AssignProcessToJobObject将新流程分配给作业对象。通过调用从ResumeThread返回的线程句柄上的CreateProcess启动运行UninstallA.exe。

然后是困难部分:等待作业对象完成其执行。不幸的是,这比任何人合理希望的要复杂得多。基本思想是创建一个I / O完成端口,然后创建对象对象,将其与I / O完成端口关联,最后等待I / O完成端口(使用{{1}获取其状态})。雷蒙德·陈(Raymond Chen)在他的blog上进行了演示(以及对此的解释)。

答案 1 :(得分:1)

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

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

是的,这太丑了。

答案 2 :(得分:1)

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

通常,孙子进程也将继承管道的写端。特别是,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;

}

答案 3 :(得分:0)

使用named mutex

答案 4 :(得分:0)

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