如何在等待另一个程序完成时更新win32 app gui?

时间:2009-02-13 16:01:45

标签: winapi clipboard subprocess

我目前正在win32 GUI应用程序中使用CreateProcess / WaitForSingleObject来启动一个处理软件许可问题的小型GUI应用程序。这一切都很好,但它在等待许可应用程序完成其工作时基本上挂起了“父”应用程序。在此期间,不会发生对父应用程序的更新,如果移动实用程序应用程序窗口,则会以丑陋的白色方块结束。

另外,由于一些奇怪的原因,当实用程序应用程序正在运行时,如果我从该应用程序中复制某些内容到剪贴板,它就会挂起。我还没弄清楚为什么,但只有在等待应用程序在父应用程序内完成时才会发生。

所以我想如果我可以让父应用程序在等待我的其他应用程序完成时处理它的事件,它可能会解决这两个问题。

那么,是否还有可以处理UI更新的CreateProcess / WaitForSingleObject的替换?

5 个答案:

答案 0 :(得分:5)

您的父进程似乎挂起,因为WaitForSingleObject()调用阻塞了您的线程,直到您传递给调用的句柄发出信号为止。

您的子进程可能会在复制到剪贴板操作期间挂起,因为作为该操作的一部分,它会专门向父进程窗口或所有顶级窗口发送消息。父进程的线程中的消息循环未运行,因为它在子进程退出时被阻止等待,因此永远不会处理该消息,并且子进程仍然被阻止。

您可以调用MsgWaitForMultipleObjects(),而不是调用WaitForSingleObject()。如果为dwWaitMask参数指定QS_ALLINPUT,则MsgWaitForMultipleObjects将在您的事件发出信号或者线程的消息队列中有输入时返回。如果由于消息可用而返回MsgWaitForMultipleObjects(),则可以处理它并继续等待:

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;
    }
}

答案 1 :(得分:3)

您可以将WaitForSingleObject调用放在循环中,并为dwMilliseconds参数使用相对较小的值。

退出循环的条件是WaitForSingleObject调用返回WAIT_OBJECT_0。

在循环中,您必须检查消息队列以查看是否有必须处理的消息队列。你如何处理这个问题取决于你的具体需求。

// Assuming hYourWaitHandle is the handle that you're waiting on
//   and hwnd is your window's handle, msg is a MSG variable and
//   result is a DWORD variable
//

// Give the process 33 ms (you can use a different value here depending on 
//  how responsive you wish your app to be)
while((result = WaitForSingleObject(hYourWaitHAndle, 33)) == WAIT_TIMEOUT)
{ 
   // if after 33 ms the object's handle is not signaled..       

   // we examine the message queue and if ther eare any waiting..
   //  Note:  see PeekMessage documentation for details on how to limit
   //         the types of messages to look for
   while(PeekMessage(&msg, hwnd,  0, 0, PM_NOREMOVE))
   {
     // we process them..
     if(GetMessage(&msg, NULL, 0, 0) > 0)
     {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
     }
   }
} 
// if you're here it means WaitForSingleObject returned WAIT_OBJECT_0, so you're done
//  (but you should always check and make sure it really is WAIT_OBJECT_0)
if(result != WAIT_OBJECT_0)
{
    // This should not be.. so react!
}

答案 2 :(得分:1)

我建议您按照以下方式处理:

  • 父应用程序执行CreateProcess,然后立即返回而不是等待响应或实用程序应用程序完成
  • 由于父申请已经返回,它可以处理其他窗口消息(例如WM_PAINT)
  • 当实用程序应用程序完成时,它会通知父应用程序(例如,使用PostMessage和RegisterWindowMessage API)
  • 父应用程序处理通过PostMessage收到的正面通知
  • 父应用程序也可能运行Windows计时器(WM_TIMER),以便它在发送通知之前知道实用程序应用程序是否已被终止

答案 3 :(得分:0)

如果您正在产生的应用导致发送消息广播(无论是显式还是隐式),您都会遇到挂起问题。这是从我的网站剪辑:

出现问题的原因是您的应用程序有一个窗口但不会传送消息。如果生成的应用程序使用其中一个广播目标(HWND_BROADCAST或HWND_TOPMOST)调用SendMessage,则SendMessage将不会返回到新应用程序,直到所有应用程序都处理了该消息 - 但您的应用程序无法处理该消息,因为它不是'抽信息....所以新的应用程序锁定,所以你的等待永远不会成功....死锁。

我不做剪贴板代码,但如果这导致上面的情况(可信),那么你就会死锁。你可以:

  • 将辅助应用程序的启动放入一个小线程
  • 使用超时并旋转PeekMessage循环(yuck)
  • 使用MsgWaitForMultipleObjects API。

顺序没有隐含的偏好...我假设你不自己创建衍生的应用程序,在这种情况下你可以使用IPC解决这个问题,正如ChrisW建议的那样。

答案 4 :(得分:0)

您应该创建一个仅执行以下操作的线程:

  • 调用CreateProcess()来运行其他应用程序

  • 调用WaitForSingleObject()等待进程完成 - 因为这是一个后台线程,你的应用程序不会阻止

  • 在流程句柄上调用CloseHandle()

  • 使用主线程的通知消息调用PostMessage()

现在,您只需要确保您的主应用程序已禁用其GUI以防止重入问题,可以通过显示模式对话框来通知用户其他应用程序正在运行且需要先处理。确保无法手动关闭对话框,并且当它从后台线程收到已发布的通知消息时它会自行关闭。您也可以将整个线程创建放入此对话框中,并将所有内容包装在一个函数中,该函数创建,显示和销毁对话框并返回外部应用程序生成的结果。