当应用程序菜单具有焦点时,消息循环被阻止

时间:2009-09-19 20:25:04

标签: c++ windows visual-studio-2008 blocking

我正在开发一个看起来像这样的应用程序:

while (true)
{
    while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
    {
       TranslateMessage(&Msg);
       DispatchMessage(&Msg);
    }
    DoSomething();
    Sleep(1);
}

我注意到当我点击菜单栏(显示菜单选项)时,不会调用DoSomething()。我观察到DispatchMessage调用阻止了messae循环,直到我退出菜单栏!

我怎么能避免这种行为?

谢谢!

2 个答案:

答案 0 :(得分:2)

原因是因为Windows在显示应用程序菜单或消息框之类的内容时接管消息处理,而Windows使用的消息循环不会调用您的DoSomething()方法。这可能很难想象,所以我会尝试逐步完成正在发生的事情:

  • 当有人打开您的菜单时,会向您的窗口发送一条消息,告诉它绘制窗口。与所有其他消息一样,DispatchMessage()会将消息发送到您的WndProc
  • 由于您未处理此邮件,因此会将其传递给Windows(因为WndProc可能会调用DefWindowProc
  • 作为默认操作,Windows绘制菜单并启动另一个默认消息循环,它不会调用DoSomething()
  • 此循环获取发往您的应用程序的消息,并通过调用WndProc将它们发送到您的应用程序,因此您的应用程序不会冻结并继续运行(减去DoSomething()调用)。
  • 菜单关闭后,控件将返回到您的消息循环(此时此刻DispatchMessage()来电将从最开始返回)

换句话说,当显示菜单时,您的消息循环将替换为默认的消息循环(例如)

while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
} 

,正如您所看到的,它不会调用您的DoSomething()方法。

要对此进行测试,请在没有显示菜单的情况下尝试在调试器中暂停代码。如果你看到callstack,你会看到当显示一个菜单时,消息正由Windows消息循环处理,而不是由你的消息循环处理。

我能想到的唯一解决方法(没有多线程)是你启动一个计时器并通过调用WM_TIMER处理DoSomething()消息,但这不是一个完美的解决方案(因为我认为你的意图是仅在没有剩余消息需要处理时调用DoSomething()

答案 1 :(得分:0)

关闭Transla并将Msg调度到一个单独的线程中。

只要DoSomething不依赖于Disminching the Msg。

虽然我可能想了解Dispatch阻止的原因;这是预期的正常行为吗?