我的应用程序(我正在处理的安装程序的bootstrap应用程序需要启动一些其他应用程序(我的安装程序和第三方安装程序以满足我的安装程序的先决条件)并等待它们完成。为了允许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 (e.g. 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();
我遇到的问题是,在某些时候,这个消息泵似乎释放了我在运行此代码时打开的窗口上的窗口和菜单句柄。我在调试器中做了一遍,并且它从来没有进入if(!PumpMessage())语句的主体,所以我不知道这里发生了什么导致窗口和菜单句柄去南。如果我没有消息泵,一切正常,除了在等待循环运行时GUI无法自行更新。
有没有人对如何使这项工作有任何想法?或者,如果bWait为TRUE,我想启动一个工作线程来启动第二个应用程序,但我以前从未对线程做过任何事情,所以我需要一些关于如何做而不引入同步问题的建议,等等(在任何一种情况下都会非常感谢代码示例。)
答案 0 :(得分:2)
我也在微软论坛上发布了这个问题,感谢微软的一个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
答案 1 :(得分:1)
我认为您的问题出在WaitForSingleObject
查看MSDN,您会看到这个
调用等待函数和直接或间接创建窗口的代码时要小心。如果一个线程创建了任何窗口,它必须处理消息。消息广播被发送到系统中的所有窗口。使用没有超时间隔的等待函数的线程可能会导致系统死锁。间接创建窗口的两个代码示例是DDE和CoInitialize函数。因此,如果您有一个创建窗口的线程,请使用MsgWaitForMultipleObjects或MsgWaitForMultipleObjectsEx,而不是WaitForSingleObject。
在我的消息泵代码中,使用MsgWaitForMultipleObjects
(doc)。
拨打此电话。
MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess, FALSE, 100, QS_ALLEVENTS);
这可以解决资源消失的问题。
答案 2 :(得分:0)
当你说窗口和菜单句柄似乎被释放时,你的意思是你有实际的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():这不会对安装程序产生任何负面影响。
当然,这都是一种猜测,但听起来似乎有道理......
答案 3 :(得分:0)
有一个名为DisableProcessWindowsGhosting的Windows函数(请参阅http://msdn.microsoft.com/en-us/library/ms648415(v=vs.85).aspx),可防止Windows“重影”您的窗口,并继续更新窗口(动画)。