Winapi 在等待其他程序完成时,如何更新win32应用程序gui?
我目前正在win32 GUI应用程序中使用CreateProcess/WaitForSingleObject来启动一个处理软件许可问题的小型GUI应用程序。这一切都可以正常工作,但它实际上会在等待授权应用程序完成其工作时挂起“父”应用程序。在此期间,不会对父应用程序进行更新,如果移动“实用程序应用程序”窗口,则最终会出现丑陋的白色方块Winapi 在等待其他程序完成时,如何更新win32应用程序gui?,winapi,clipboard,subprocess,Winapi,Clipboard,Subprocess,我目前正在win32 GUI应用程序中使用CreateProcess/WaitForSingleObject来启动一个处理软件许可问题的小型GUI应用程序。这一切都可以正常工作,但它实际上会在等待授权应用程序完成其工作时挂起“父”应用程序。在此期间,不会对父应用程序进行更新,如果移动“实用程序应用程序”窗口,则最终会出现丑陋的白色方块 // Assuming hYourWaitHandle is the handle that you're waiting on // and hwnd
此外,出于某种奇怪的原因,当实用程序应用程序运行时,如果我将该应用程序中的某些内容复制到剪贴板,它将挂起。我还没有弄清楚原因,但只有当我在父应用程序中等待应用程序完成时,才会发生这种情况 所以我在想,如果我能让父应用程序在等待我的另一个应用程序完成时处理它的事件,它可能会解决这两个问题// 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! }
那么,是否有同样处理UI更新的CreateProcess/WaitForSingleObject的替代品?我建议您可以如下处理:
- 父应用程序执行CreateProcess,然后立即返回,而不是等待响应或实用程序应用程序完成
- 由于父应用程序已返回,因此它可以处理其他窗口消息(例如WM_PAINT)
- 当实用程序应用程序完成时,它会通知父应用程序(例如,使用PostMessage和RegisterWindowMessage API)
- 父应用程序处理通过PostMessage接收的肯定通知
- 父应用程序也可能有一个Windows计时器(WM_timer)正在运行,以便它在发送通知之前知道实用程序应用程序是否已被杀死
- 将辅助应用程序的启动放入一个小线程中
- 使用一个超时并围绕一个消息循环旋转(恶心)
- 使用MsgWaitForMultipleObjects API
这种排序并不意味着偏好。。。我假设您自己不创建派生的应用程序,在这种情况下,您可以使用IPC来解决这个问题,正如ChrisW所建议的那样。您应该创建一个只执行以下操作的线程:
- 调用CreateProcess()运行其他应用程序
- 调用WaitForSingleObject()以等待进程完成-因为这是一个后台线程,所以应用程序不会阻止它
- 对进程句柄调用CloseHandle()
- 使用主线程的通知消息调用PostMessage()
现在,您只需确保主应用程序的GUI已禁用以防止重入问题,可以通过显示一个模式对话框来通知用户另一个应用程序正在运行并且需要首先处理。确保对话框不能手动关闭,并且当它从后台线程接收到发布的通知消息时,它会自动关闭。您还可以将整个线程创建过程放入该对话框,并将所有内容封装在一个函数中,该函数创建、显示和销毁对话框,并返回外部应用程序生成的结果。您可以将WaitForSingleObject调用放入循环中,并使用相对较小的值作为dwMillicles参数 退出循环的条件是WaitForSingleObject调用返回WAIT_OBJECT_0 在循环中,您必须检查消息队列,以查看是否有必须处理的消息。如何处理这件事完全取决于你自己,这取决于你的典型需求
父进程似乎挂起,因为WaitForSingleObject()调用会阻塞线程,直到传递到调用中的句柄发出信号为止
在复制到剪贴板操作期间,您的子进程可能会挂起,因为作为该操作的一部分,它会专门向父进程的窗口或所有顶级窗口发送消息。父进程线程中的消息循环未运行,因为它在等待子进程退出时被阻止,因此消息永远不会被处理,子进程仍然被阻止
您可以调用,而不是调用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;
}
}