我的应用程序(我正在使用的安装程序的引导程序应用程序需要启动一些其他应用程序(我的安装程序和第三方安装程序的安装程序的先决条件),并等待它们完成。为了允许GUI执行屏幕更新在等待一个应用程序完成的时候,我使用Visual Studio文档中关于空闲循环处理的'兼容MFC'的例子,在等待循环中放置了一个消息泵作为指导。我的代码(在CWinApp的成员函数中派生类)如下:
if (::CreateProcess(lpAppName, szCmdLineBuffer, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) { ::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode); if (bWait) while (dwExitCode == STILL_ACTIVE) { // In order to allow updates of the GUI to happen while we're waiting for // the application to finish, we must run a mini message pump here to // allow messages to go through and get processed. This message pump // performs much like MFC's main message pump found in CWinThread::Run(). MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!PumpMessage()) { // a termination message (eg WM_DESTROY) // was processed, so we need to stop waiting dwExitCode = ERROR_CANT_WAIT; ::PostQuitMessage(0); break; } } // let MFC do its idle processing LONG nIdle = 0; while (OnIdle(nIdle++)) ; if (dwExitCode == STILL_ACTIVE) // was a termination message processed? { // no; wait for .1 second to see if the application is finished ::WaitForSingleObject(ProcessInfo.hProcess, 100); ::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode); } } ::CloseHandle(ProcessInfo.hProcess); ::CloseHandle(ProcessInfo.hThread); } else dwExitCode = ::GetLastError();
我遇到的问题是,在某些时候,这个消息泵似乎在运行这个代码的时候,在我打开的窗口上释放了窗口和菜单句柄。 我在debugging器中做了一个遍历,并且从来没有进入if(!PumpMessage())语句的主体,所以我不知道这里发生了什么事情,导致窗口和菜单句柄南。 如果我没有消息泵,一切工作正常,除了在等待循环运行时GUI不能自行更新。
有没有人有任何想法如何使这项工作? 另外,我想启动一个工作线程来启动第二个应用程序,如果bWait为TRUE,但我从来没有做过任何线程之前,所以我需要一些build议如何做到这一点,而不引入同步问题等(代码示例在任何情况下都将非常感激)。
我也在微软的论坛上发布了这个问题,并且感谢微软的Doug Harris的帮助,我发现我的HWND和HMENU值的问题确实是由于陈旧的CWwnd *和CMenu *指针GetMenu()和GetDialogItem()调用,在启动第二个应用程序之后再次获取指针解决了这个问题,并且指出了一个网站*,它显示了使用MsgWaitForMultipleObjects()控制它的一个更好的方法,不需要等待一定的时间,然后轮询退出代码。
我的循环现在看起来像这样:
if (bWait) { // In order to allow updates of the GUI to happen while we're // waiting for the application to finish, we must run a message // pump here to allow messages to go through and get processed. LONG nIdleCount = 0; for (;;) { MSG msg; if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) PumpMessage(); else //if (!OnIdle(nIdleCount++)) { nIdleCount = 0; if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { DWORD nRes = ::MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess, FALSE, INFINITE, QS_ALLEVENTS); if (nRes == WAIT_OBJECT_0) break; } } } } ::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
*那个网站,如果你好奇,是: http : //members.cox.net/doug_web/threads.htm
我认为你的问题是在WaitForSingleObject
看在MSDN你看到这个
调用直接或间接创建窗口的等待函数和代码时要小心。 如果线程创建任何窗口,它必须处理消息。 消息广播被发送到系统中的所有窗口。 使用等待函数没有超时间隔的线程可能会导致系统死锁。 间接创建窗口的两个代码示例是DDE和CoInitialize函数。 因此,如果您有创建窗口的线程,请使用MsgWaitForMultipleObjects或MsgWaitForMultipleObjectsEx,而不是WaitForSingleObject。
在消息泵的代码中使用使用MsgWaitForMultipleObjects
( doc )。
随着这个电话的呼叫。
MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess, FALSE, 100, QS_ALLEVENTS);
这应该停止资源消耗的问题。
当你说窗口和菜单句柄似乎被释放,你的意思是你有实际的HWND和HMENU值不再似乎工作,或者你有MFC的CWnd *和CMenu *变量失败?
如果是后者,问题很可能是通过调用CWnd :: FromHandle()(或CMenu :: FromHandle())某处(或调用某些调用它们的东西)来获取CWnd *指针,并且OnIdle()是丢弃它们。
根本原因是MFC维护从系统中的CWnd *对象的窗口(或菜单等)句柄的地图。 当CWnd :: FromHandle()被调用时,它会在映射中查找一个匹配项:如果找到了,就返回。 如果不是,则创建一个新的临时CWnd,并将其添加到地图中,然后返回。 OnIdle()背后的思想是当所有的消息处理完成后,OnIdle()放弃所有仍然存在的临时CWnd对象。 这就是为什么CWnd :: FromHandle()文档警告返回的指针可能是临时的。
这个“正确的”解决方案是不挂在从CWnd :: FromHandle()返回的CWnd *指针上。 鉴于您的应用程序的简单性,删除OnIdle()函数可能更容易:这不会对安装程序产生任何负面影响。
当然,这都是猜测,但这听起来似乎是合理的。
有一个名为DisableProcessWindowsGhosting的Windows函数(请参阅http://msdn.microsoft.com/zh-cn/library/ms648415(v=vs.85).aspx ),它可以防止Windows“重影”窗口,并继续更新窗口(你的动画)。