1

I am firing off a process with CreateProcess, and I want to wait for the process to finish, or for anything to be written to standard out, which is being piped through an anonymous pipe. The code below doesn't work, as WaitForMultipleObjects keeps returning for the stdout pipe, even though there is nothing to read. Is there a way to wait for a pipe? I can't wait for a read, as I need to also continue if the process finishes. I also cannot wait until the process is complete without checking the pipe, as it might overflow. Any ideas?

if (::CreateProcess(
    (!application_name.empty() ? application_name.c_str() : NULL),  // Application/Executable name, if supplied.
    lpCommandLine,                                                  // Arguments or Executable and Arguments
    NULL,                               // Process Attributes
    NULL,                               // Thread Attributes
    TRUE,                               // Inherit handles
    CREATE_NO_WINDOW,                   // Create flags
    NULL,                               // Environment (Inherit)
    current_directory.c_str(),          // Working Directory
    &m_startup_info,                    // Startup Info
    &process_info                       // Process Info
))
{
    HANDLE  handles[2];
    bool    waiting = true;

    handles[0] = process_info.hProcess;
    handles[1] = m_read_stdout; // previously created with CreatePipe. One end handed to CreateProcess

    // Must process stdout otherwise the process may block if it's output buffer fills!!
    while (waiting)
    {
        DWORD r = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);

        switch (r)
        {
        case WAIT_OBJECT_0+0:
            waiting = false;
            break;
        case WAIT_OBJECT_0+1:
            AppendSTDOUTFromProcess(output);
            break;
        default:
            ATLASSERT(FALSE);
            break;
        }
    }
}
Simon Parker
  • 1,656
  • 15
  • 37

1 Answers1

5

Pipes are not waitable objects, so you cannot use them in the WaitFor...() functions. You can either:

  1. Use WaitForSingleObject() to wait on just the process handle, and give it a timeout so your loop wakes up periodically, then it can call PeekNamedPipe() to check the pipe for data.

  2. Do the pipe reading in a separate thread, letting the reading block when no data is available, and then terminate the thread when the pipe is closed. Then you can use WaitForMultipleObjects() to wait on the process and thread handles.

  3. Don't wait on the process handle at all. Just have your loop read from the pipe, blocking when no data is available, until the reading fails when the pipe is closed. This is the approach Microsoft's example uses.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I went with the first option. It fit best with the code as it was. Thanks. – Simon Parker Feb 22 '16 at 05:54
  • Hi Remy, I have a question. If you do the pipe reading in a separate thread, how do you interrupt it (say if you need to cancel the operation, or gracefully exit the current process)? – user541686 Apr 05 '20 at 12:51