Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for grandchild processes in windows

Tags:

winapi

Is it possible to wait for all processes launched by a child process in Windows? I can't modify the child or grandchild processes.

Specifically, here's what I want to do. My process launches uninstallA.exe. The process uninistallA.exe launches uninstallB.exe and immediately exits, and uninstallB.exe runs for a while. I'd like to wait for uninstallB.exe to exit so that I can know when the uninstall is finished.

like image 699
thudbang Avatar asked Nov 05 '09 21:11

thudbang


People also ask

How do you wait for all children's processes?

Just use: while(wait(NULL) > 0); This ensures that you wait for ALL the child processes and only when all have returned, you move to the next instruction.

Does wait () wait for all child processes?

A call to wait() blocks the calling process until one of its child processes exits or a signal is received. After child process terminates, parent continues its execution after wait system call instruction.

Does wait wait for grandchildren?

No, it waits until a child (any child) of the process terminates, not all of them. but if u have a child that has another child does it wait until all these are exited? wait() only worries about the children of the process. It doesn't care about its grandchildren.

What happens if you use wait () in the child?

Suspends the calling process until any one of its child processes ends. More precisely, wait() suspends the calling process until the system obtains status information on the ended child. If the system already has status information on a completed child process when wait() is called, wait() returns immediately.


2 Answers

Create a Job Object with CreateJobObject. Use CreateProcess to start UninstallA.exe in a suspended state. Assign that new process to your job object with AssignProcessToJobObject. Start UninstallA.exe running by calling ResumeThread on the handle of the thread you got back from CreateProcess.

Then the hard part: wait for the job object to complete its execution. Unfortunately, this is quite a bit more complex than anybody would reasonably hope for. The basic idea is that you create an I/O completion port, then you create the object object, associate it with the I/O completion port, and finally wait on the I/O completion port (getting its status with GetQueuedCompletionStatus). Raymond Chen has a demonstration (and explanation of how this came about) on his blog.

like image 88
Jerry Coffin Avatar answered Oct 14 '22 20:10

Jerry Coffin


Here's a technique that, while not infallible, can be useful if for some reason you can't use a job object. The idea is to create an anonymous pipe and let the child process inherit the handle to the write end of the pipe.

Typically, grandchild processes will also inherit the write end of the pipe. In particular, processes launched by cmd.exe (e.g., from a batch file) will inherit handles.

Once the child process has exited, the parent process closes its handle to the write end of the pipe, and then attempts to read from the pipe. Since nobody is writing to the pipe, the read operation will block indefinitely. (Of course you can use threads or asynchronous I/O if you want to keep doing stuff while waiting for the grandchildren.)

When (and only when) the last handle to the write end of the pipe is closed, the write end of the pipe is automatically destroyed. This breaks the pipe and the read operation completes and reports an ERROR_BROKEN_PIPE failure.

I've been using this code (and earlier versions of the same code) in production for a number of years.

// 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;

}
like image 22
Harry Johnston Avatar answered Oct 14 '22 20:10

Harry Johnston