views:

562

answers:

1

Everyone probably knows the code to run a program and wait for it to end:

   CreateProcess(...
   WaitForSingleObject(Process.hProcess, INFINITE

It was used several times by myself. But recently I found that this call when it launches a program with a multimedia playback has worse performance than the same process being executed from a general file manager (Windows XP). That's ok with CPU consumption of my (parent) process, but while playing the fragment there are unexpected little stops.

I made a little change to something like:

CreateProcess ...
do {
    Sleep(100);
    Res = WaitForSingleObject(Process.hProcess, 10);
} while (Res == WAIT_TIMEOUT);

And it helped. Now the child process plays the fragment without problems. So what's wrong with the first fragment and is it documented somewhere? As I see from the tests the second "wait" is more "relaxed" than the first one, but the first one doesn't eat CPU at least formally

+2  A: 

If this code is running on a UI thread, you will cause performance problems with other processes that (directly or indirectly) send messages to your window(s), since you do not run the message loop while you are waiting for the child process. Neither Sleep() nor WaitForSingleObject() will process messages.

Windows Explorer (the file manager) will not suffer this problem because it:

  1. Does not keep an open handle to processes it launches at the user's request (I think this is more likely, since Explorer needs neither to know when the process exits or its exit code), or
  2. Does not wait on any open process handles it may keep from its children, and importantly
  3. Always makes sure to run a message loop while it waits on handles. This is very important in any process that makes use of COM, which Explorer uses heavily.

Instead of calling WaitForSingleObject(), you can call MsgWaitForMultipleObjects(). If you specifiy QS_ALLINPUT for the dwWaitMask parameter, MsgWaitForMultipleObjects will return either when your event is signaled or when there is input in the thread's message queue. If MsgWaitForMultipleObjects() returned because a message is available, you can process it and resume waiting:

MSG msg;
DWORD reason = WAIT_TIMEOUT;
while (WAIT_OBJECT_0 != reason) {
    reason = MsgWaitForMultipleObjects(1, &hChildProcess, FALSE, INFINITE, QS_ALLINPUT);
    switch (reason) {
    case WAIT_OBJECT_0:
        // Your child process is finished.
        break;
    case (WAIT_OBJECT_0 + 1):
        // A message is available in the message queue.
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            // Note that if your main message loop does additional processing
            // (such as calling IsDialogMessage() for modeless dialogs)
            // you will want to do those things here, too.
        }
        break;
    }
}
Matthew Xavier
You're looking at the wrong side. Apparently, the child process has a worse performance if the parent is blocked waiting. WTF indeed.
MSalters
Slight buglet in this answer: the link pointed to by `MsgWaitForMultipleObjects()` actually points to the MSDN documentation of the Sleep() function. Kinda funny in a way. ;-)
Frerich Raabe