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

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

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

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

我怎么能避免这种行为?

谢谢!

之所以这样,是因为当Windows显示应用程序菜单或消息框之类的东西时,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消息循环处理,而不是你的。

我可以想到的唯一的解决方法(没有多线程)是,如果你启动一个计时器,并通过调用DoSomething()处理WM_TIMER消息,但这不会是一个完美的解决方案(因为我认为你的意图是调用DoSomething()没有留言可供处理)。

关闭翻译和分发Msg到一个单独的线程。

只要DoSomething不依赖于调度消息。

虽然我可能想明白为什么调度被阻塞; 这是预期的正常行为?