3

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

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

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

那么,是否有替代 CreateProcess/WaitForSingleObject 来处理 UI 更新?

4

5 回答 5

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;
    }
}
于 2009-02-17T14:55:08.400 回答
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!
}

于 2009-02-13T17:07:53.413 回答
1

我建议你可以这样处理:

  • 父应用程序执行 CreateProcess,然后立即返回,而不是等待响应或实用程序应用程序完成
  • 因为父应用程序已经返回,它可以处理其他的Window消息(例如WM_PAINT)
  • 当实用程序应用程序完成时,它会通知父应用程序(例如使用 PostMessage 和 RegisterWindowMessage API)
  • 父应用程序处理通过 PostMessage 收到的肯定通知
  • 父应用程序也可能有一个 Windows 计时器 (WM_TIMER) 正在运行,以便它知道实用程序应用程序是否在发送通知之前被终止
于 2009-02-13T16:10:36.640 回答
0

如果您生成的应用程序导致发送消息广播,无论是显式的还是隐式的,您都可能会遇到挂起问题。这是从我的网站上截取的:

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

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

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

该排序不暗示偏好...我假设您不自己创建衍生的应用程序,在这种情况下,您可以使用 IPC 来解决此问题,正如 ChrisW 建议的那样。

于 2009-02-13T16:14:13.090 回答
0

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

  • 调用 CreateProcess() 来运行另一个应用程序

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

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

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

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

于 2009-02-13T16:48:44.413 回答