Waiting for grandchild processes in windows

Can I wait for all processes started by a child process in Windows? I cannot change the processes of a child or grandchildren.

In particular, this is what I want to do. In my process, uninstallA.exe starts. The uninistallA.exe process starts uninstallB.exe and exits immediately, and uninstallB.exe runs for a while. I want to wait until uninstallB.exe exits to find out when uninstall is complete.

+7
winapi
source share
5 answers

Create a job object using CreateJobObject . Use CreateProcess to start UninstallA.exe in a suspended state. Assign a new process to your job with AssignProcessToJobObject . Run UninstallA.exe by calling ResumeThread on the thread descriptor that you received from CreateProcess .

Then the hard part: wait for the completion of the task object. Unfortunately, this is quite a bit more complicated than one would hope. The main idea is that you create an I / O completion port, then create an object object, bind it to the I / O completion port, and finally wait on the I / O completion port (getting its status using GetQueuedCompletionStatus ), by Raymond Chen has a demonstration (and an explanation of how this happened) on his blog .

+9
source share

There is no general way to wait for all the grandchildren, but for your specific case, you can hack something together. You know that you are looking for a specific process instance. First, I have to wait for uninstallA.exe to exit (using WaitForSingleObject), because at this point you know that uninstallB.exe is running. Then use EnumProcesses and GetProcessImageFileName from the PSAPI to find the executable instance of uninstallB.exe. If you have not found this, you know that it is already finished, otherwise you can wait.

An additional complication is that if you need to support versions of Windows older than XP, you cannot use GetProcessImageFileName, and for Windows NT you cannot use PSAPI at all. In Windows 2000, you can use GetModuleFileNameEx, but there are some caveats in it that mean that sometimes this may fail (check the docs). If you need to support NT, then look at Toolhelp32.

Yes, this is super ugly.

+1
source share

Here is a technique that, although not infallible, can be useful if, for some reason, you cannot use the task object. The idea is to create an anonymous pipe and allow the child process to inherit the handle to the end of the pipe.

Typically, grandchild processes also inherit the end of a pipe record. In particular, processes started with cmd.exe (for example, from a batch file) inherit descriptors.

After the child process exits, the parent process closes its handle to the end of the recording in the pipe and then tries to read from this channel. Since no one writes 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 things while waiting for grandchildren.)

When (and only when) the last descriptor at the end of the pipe record is closed, the end of the record will be automatically destroyed. This breaks the channel and the read operation completes and reports an ERROR_BROKEN_PIPE error.

I have been using this code (and earlier versions of the same code) in production for several 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; } 
+1
source share
0
source share

One option is to install Cygwin and then use the ps command to watch the grandson exit

0
source share

All Articles