Henry
Henry

Reputation:

Message loop gets blocked when application menu has the focus

I'm developing an application that looks mainly like this:

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

What I noticed is that DoSomething() doesn't get called when I click on the menu bar (displaying menu options). I've observed that DispatchMessage call blocks the messae loop until I get out of the menu bar!

How could I avoid this behaviour??

Thanks!

Upvotes: 3

Views: 2266

Answers (2)

GRB
GRB

Reputation: 4022

The reason why is because Windows takes over the processing of messages when something like an application menu or message box is displayed, and that message loop which Windows uses won't call your DoSomething() method. This may be hard to visualize, so I'll try to step through what is happening:

  • When someone opens your menu, a message is sent to your window telling it to draw the window. DispatchMessage() sends the message to your WndProc, like all other messages.
  • Since you don't handle this message, it is passed to Windows (since your WndProc more than likely calls DefWindowProc)
  • As the default operation, Windows draws the menu and starts another default message loop which will not call DoSomething()
  • This loop fetches the messages destined for your application and dispatches them to your application by calling WndProc, so your application doesn't freeze and continues to operate (minus the DoSomething() call).
  • Once the menu is closed, control will be returned to your message loop (only at this point will the DispatchMessage() call from the very beginning return)

In other words, when a menu is displayed, your message loop is replaced by a default one which looks like this (for example)

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

which, as you can see, won't call your DoSomething() method.

To test this, try pausing your code in a debugger when no menu is displayed, and while one is. If you see the callstack, you will see that when a menu is displayed, messages are being processed by a Windows message loop, not by yours.

The only workaround I can think of (without multithreading) is if you start a timer and handle the WM_TIMER message by calling DoSomething(), but that would not be a perfect solution (since I presume your intent is to call DoSomething() only when there are no messages left to process).

Upvotes: 3

simon
simon

Reputation: 5935

Spin off Translating and Dispatching the Msg into a separate Thread.

As long as DoSomething isn't dependent on Dispatching the Msg.

Although I might want to understand why the Dispatch is blocking; is this expected normal behavior?

Upvotes: -1

Related Questions