【问题标题】:How can I update a win32 app gui while it is waiting for another program to finish?如何在等待另一个程序完成时更新 win32 应用程序 gui?
【发布时间】:2010-10-07 11:37:51
【问题描述】:

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

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

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

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

【问题讨论】:

  • 如果您要监控的应用程序不受您的控制,您不能使用需要它向您的父应用程序发送或发布消息的解决方案。从您的问题中不清楚您是否可以更改许可应用程序。

标签: winapi clipboard subprocess


【解决方案1】:

您的父进程似乎挂起,因为 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;
    }
}

【讨论】:

    【解决方案2】:

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

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

    在循环中,您必须检查消息队列以查看是否有任何您必须处理的内容。如何处理这件事完全取决于您,这取决于您的典型需求。

    <pre></pre>

    // 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!
    }
    

    【讨论】:

    • 这是保持应用程序响应的最简单方法。
    【解决方案3】:

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

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

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

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

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

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

      【讨论】:

        【解决方案5】:

        我建议你可以这样处理:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-30
          • 1970-01-01
          • 1970-01-01
          • 2014-04-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多