views:

623

answers:

5

I am currently using CreateProcess/WaitForSingleObject from within a win32 GUI app to launch a small GUI app that deals with software licensing issues. This all works fine, but it essentially hangs the "parent" app while it waits for the licensing app to finish its work. During this time, updates to the parent app do not occur and it ends up with ugly white squares if the utility app window is moved.

Also, for some strange reason, while the utility app is running, if I copy something from within that app to the clipboard, it HANGS. I haven't figured out why yet, but it only happens if I am waiting for the app to finish from within the parent app.

So I'm thinking that if I can cause the parent app to handle its events while waiting for my other app to finish, it might solve both problems.

So, is there a replacement for CreateProcess/WaitForSingleObject that also handles UI updates?

+1  A: 

I suggest you can handle this as follows:

  • Parent application does CreateProcess, and then returns immediately instead of waiting for a response or for the utility app to finish
  • Because the parent applicatin has returned, it can handle other Window messages (e.g. WM_PAINT)
  • When the utility app finishes, it notifies the parent application (e.g. using PostMessage and RegisterWindowMessage APIs)
  • Parent application handles positive notification received via PostMessage
  • Parent application may also have a Windows timer (WM_TIMER) running, so that it knows if the utility app is killed before it send its notification
ChrisW
A: 

You can get a hanging problem if the app you are spawning causes a sendmessage broadcast, either explicit or implicit. This is clipped from my website:

The problem arises because your application has a window but isn't pumping messages. If the spawned application invokes SendMessage with one of the broadcast targets (HWND_BROADCAST or HWND_TOPMOST), then the SendMessage won't return to the new application until all applications have handled the message - but your app can't handle the message because it isn't pumping messages.... so the new app locks up, so your wait never succeeds.... DEADLOCK.

I don't do clipboard code, but if that causes the situation above (believeable), then you'll deadlock. You can:

  • put the launch of the secondary application into a little thread
  • use a timeout and spin around a PeekMessage loop (yuck)
  • use the MsgWaitForMultipleObjects API.

No preference is implied by that ordering... I'm assuming you don't create the spawned application yourself, in which case you could use IPC to get around this issue, as ChrisW suggested.

Bob Moore
A: 

You should create a thread that does only the following:

  • call CreateProcess() to run the other app

  • call WaitForSingleObject() to wait for the process to finish - since this is a background thread your app will not block

  • call CloseHandle() on the process handle

  • call PostMessage() with a notification message for your main thread

Now you only need to make sure that your main application has its GUI disabled to prevent reentrancy problems, possible by showing a modal dialog that informs the user that the other app is running and needs to be dealt with first. Make sure that the dialog can not be closed manually, and that it closes itself when it receives the posted notification message from the background thread. You can put the whole thread creation into this dialog as well, and wrap everything in a single function that creates, shows and destroys the dialog and returns the result your external application produces.

mghie
+3  A: 
Miky Dinescu
This is the simplest way to keep the application responsive.
jussij
+3  A: 

Your parent process appears to hang because the WaitForSingleObject() call blocks your thread until the handle you pass into the call is signaled.

Your child process likely hangs during the copy-to-clipboard operation because it is, as a part of that operation, sending a message either specifically to the parent process's window or to all top-level windows. The message loop in your parent process's thread is not running, because it is blocked waiting until the child process exits, so the message is never processed and the child process remains blocked.

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