views:

245

answers:

2

Hi, 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!

A: 

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?

simon
It's not so easy because window messages must be handled inside the thread the window was created in.Moreover, DoSomething does some opengl rendering and call to swapbuffers, so opengl rendering could happen at any time and I think that application would crash.
Henry
A: 

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).

GRB
Great! It works! One thousand of thanks ;)
Henry
GetMessage() returns a BOOL, but it actually has a tri-state return value. If it returns -1 (to signal an error), then your code snippet will probably crash or loop infinitely.
bk1e
Sorry, you're right, I forgot that. Edited in a `> 0` check as I normally have it.
GRB
Could those voting me down please suggest what I'm doing wrong?
GRB