March 1999

Waiting on a spawned process

by Kent Reisdorph

Sometimes you want to spawn an external process and wait until it's completed before continuing with your application. Both CreateProcess and ShellExecuteEx give you a handle to the thread Windows created to run the spawned application. You can use this thread handle, in conjunction with the WaitForSingleObject API function, to pause execution of your program until the spawned application has terminated. To do so, simply pass the thread handle of the spawned process to WaitForSingleObject and Windows does the rest. Here's how the code would look if you're using the CreateProcess function:

STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
int res = CreateProcess(0, "notepad.exe",
  0, 0, 0, 0, 0, 0, &si, &pi);
if (res) {
  WaitForSingleObject(pi.hThread,  
    INFINITE);
  SetFocus();
}
If CreateProcess is successful, the hThread member of the PROCESS_INFORMATION structure contains the thread handle for the new process. We pass that thread handle to WaitForSingleObject with a timeout value of INFINITE. The host application will now pause execution until the spawned application is closed. ShellExecuteEx can also be used to wait for a spawned process. Here's an example of displaying a text file in Windows Notepad and waiting for Notepad to close before allowing the application to continue:
SHELLEXECUTEINFO si;
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(si);
si.hwnd = Handle;
si.lpVerb = "open";
si.lpFile = "test.txt";
si.nShow = SW_NORMAL;
si.fMask = SEE_MASK_NOCLOSEPROCESS;
bool res = ShellExecuteEx(&si);
if (res)
  WaitForSingleObject(si.hProcess,
    INFINITE);
Although we haven't talked about ShellExecuteEx in any detail, it's easy to see how this function works compared to ShellExecute. Rather than sending the parameters directly to the function, ShellExecuteEx requires you to fill out a structure with the required information. If ShellExecuteEx returns successfully, the hProcess member of the SHELLEXECUTEINFO structure contains the process handle of the spawned application. This member is only valid if the fMask member contains the SEE_MASK_NOCLOSEPROCESS flag. You can pass the process handle to WaitForSingleObject, as shown in the previous example.