2

I have encountered a, so it seems to me, strange behaviour of the WinAPI. In my program I am setting a timer for the window with

::SetTimer(window_handle, timer_id, 10, NULL);

and handle the WM_TIMER message in my window procedure. To reduce the amount of cpu time needed I am also using the ::WaitMessage function in my message pump. Turns out now that, as long as I have the ::WaitMessage function there, the WM_TIMER messages just stop coming after a while. If I remove it from my message pump everything works just fine as expected.

Now I wonder wether I set up my timer wrong or if this is standard behaviour of ::WaitMessage. Searching MSDN and the web did not give me an idea why this is like this.

Here is the message pump I use:

while(true) {
    if(GetMessage(&msg, _window_handle, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } else { 
        return 0; 
    }

    WaitMessage();
}

Hope that someone can clear this up for me.

Cyianor
  • 45
  • 5
  • I don't understand why you think that using `WaitMessage` will decrease CPU time. It seems to me that constantly calling `WaitMessage` would just make it likely that you'll fill up your message queue, which might explain why you you eventually start losing `WM_TIMER` messages. – jamesdlin Apr 13 '12 at 10:05
  • That is how I understand the [explanation on MSDN](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644956%28v=vs.85%29.aspx): `Yields control to other threads when a thread has no other messages in its message queue. The WaitMessage function suspends the thread and does not return until a new message is placed in the thread's message queue.` And according to the Windows Taskmanager the percentage of CPU usage really is decreasing when using this function. – Cyianor Apr 13 '12 at 10:09
  • 3
    Yes, but `GetMessage` already waits for messages if the message queue is empty. Unless you explicitly need to yield control to another thread while you still have pending messages, calling `WaitMessage` for every message doesn't seem useful. Also see the remark about what's considered a "new" message. – jamesdlin Apr 13 '12 at 10:11
  • 1
    You should fetch messages in a loop, because `WaitMessage` will block even if there are messages waiting. `WaitMessage` doesn't return until there is a _new_ message coming in. This means that if there is messages in the queue when you call `WaitMessage` and then you get a `WM_TIMER` event, the next message you get will _not_ be the `WM_TIMER` message but the next unread message in the queue. – Some programmer dude Apr 13 '12 at 10:14
  • Ok, I guess I unterstood that part about what is considered a new message wrong. Thank you! This also explains why the `WM_TIMER` message started coming again if I moved the mouse over the window. Care to turn it into an answer that I can accept? – Cyianor Apr 13 '12 at 10:15
  • 1
    Um, when `GetMessage` returns 0, it means that you got a `WM_QUIT` message. Don't wait for another message; just exit! – Raymond Chen Apr 13 '12 at 17:05
  • I don't wait for another message if GetMessage returns 0. I check if it is `> 0` and exit the loop if it is not. But I removed `WaitMessage` now anyways. – Cyianor Apr 13 '12 at 19:50

2 Answers2

8

Yes, this will randomly fail to process more timer messages. A pretty hard rule for WaitMessage() is that the message queue should be empty before you call it. If it isn't empty then any messages left in the queue are marked as "seen" and WaitMessage() ignores them.

So the failure scenario is having two messages in the queue, say a mouse message and a timer message. You get the mouse message but leave the timer message. No additional timer message is generated since you haven't processed the pending one. The combination of GetMessage + WaitMessage is very troublesome, you'd have to use PeekMessage instead.

Just remove WaitMessage(), it serves no useful purpose here.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • This [Post](https://dblohm7.ca/blog/2015/03/12/waitmessage-considered-harmful/) also suggests that WaitMessage might even have problem with PeekMessage and cites the sentence from the documentation of the WaitMessage. – Plastic Jun 28 '23 at 20:37
  • So you say WaitMessage marks all the messages in the queue as seen and then it yields? I think it first checks whether any unseen message is present to decide to yield or not; If there are unseen messages it marks all the unseen messages as seen but won't yield control. Also WaitMessage documentation says that PeekMessage, GetMessage, and WaitMessage check the queue and then change the state information for the queue so that the input is no longer considered new. This supports my theory about WaitMessage, and also suggest that PeekMessage+WaitMessage is no better than GetMessage+WaitMessage. – Plastic Jun 28 '23 at 22:54
0

In addition to what Hans Passant said, you should reword the code. WaitMessage doesn't play well with GetMessage, use PeekMessage instead and keep WaitMessage only in the alternative code path (else), like so:

while( true )
{
    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        WaitMessage();
    }
}

WaitMessage allows a thread to sleep until a message is in the queue.

KeyC0de
  • 4,728
  • 8
  • 44
  • 68
  • Thank you for the additional answer. It has been nine years since the question was posted, so I will leave the original formulation of the question untouched. Thank you for pointing this out though and maybe it helps somebody else. – Cyianor Apr 06 '21 at 11:47