Even though mouse-move, paint, and timer messages are generated on demand, it’s still possible for one to end up in your queue


We all know that the generated-on-demand messages like WM_MOUSE­MOVE, WM_PAINT, and WM_TIMER messages are not posted into the queue when the corresponding event occurs, but rather are generated by Get­Message or Peek­Message when they detect that they are about to conclude that there is no message to return and the generated-on-demand message can be returned. When this happens, the window manager creates the message on the fly, posts it into the queue, and hey, how about that, the Get­Message or Peek­Message function now has a message to return!

Note that this auto-generate can happen even though the queue is not empty, because the message filters control what messages in the queue can be returned. For example, suppose the message queue contains the following messages:

  • { hwnd1, WM_CLIP­BOARD­UPDATE }
  • { hwnd2, WM_LBUTTON­DOWN }

(Note that the above diagram is not strictly correct, because the WM_LBUTTON­DOWN message goes into the input queue, not the message queue, but the distinction is not important here.)

Suppose you now call Get­Message(&msg, hwnd1, WM_MOUSE­FIRST, WM_MOUSE­LAST). None of the messages in the queue satisfy the message filter: The first message meets the window filter, but the message is not in range. The second message meets the message range filter, but does not meet the window filter. The Get­Message function is about to give up and say "I guess I need to wait for a message," but before it finally concedes defeat, it says, "Hang on there. I see a note that tells me that I should auto-generate a WM_MOUSE­MOVE message for window hwnd1. And that message satisfies the message filter. I'll generate it now!"

The Get­Message function posts the { hwnd1, WM_MOUSE­MOVE } message into the queue (assigning it the current time as the timestamp), and then it says, "Hey, lookie here! A message that satisfies the filter!" It then removes the message from the queue and returns it.

(Note that this algorithm is conceptual. It doesn't actually work this way internally. In particular, the window manager does not literally talk to itself, at least not out loud.)

Okay, so in the Get­Message case, even if the message conceptually goes into the queue, it comes right back out immediately, so you never actually observe it there.

Now repeat the exercise with the Peek­Message function. As before, the WM_MOUSE­MOVE message is posted into the queue with the current time as the timestamp. If the PM_REMOVE flag is passed, then the message is removed from the queue and returned, just like Get­Message. If the PM_NO­REMOVE flag is passed, then things get interesting: The message is returned but not removed from the queue.

You now have a WM_MOUSE­MOVE message physically residing in the queue!

This is the answer to the puzzle: If auto-generated messages are generated on demand, how is it possible for them to end up sitting in your message queue?

I recall a bug investigation from nearly two decades ago which basically boiled down to this issue: Somebody PM_NO­REMOVE'd an auto-generated message and not only left it in the queue, but kept generating new ones without processing the old ones. Eventually, the message queue filled up.

(Note that this is also the answer to the puzzle: If WM_MOUSE­MOVE is generated on demand, how can it be possible to retrieve a WM_MOUSE­MOVE message with a timestamp different from the current time?)

Comments (8)
  1. Joshua says:

    [(Note that this is also the answer to the puzzle: If WM_MOUSE­MOVE is generated on demand, how can it be possible to retrieve a WM_MOUSE­MOVE message with a timestamp different from the current time?)]

    Also, clock skew!

    [Not sure what you're referring to here. There is only one computer involved. If you mean signal propagation delay, then consider the question "How can it be possible to retrieve a WM_MOUSE­MOVE message with a timestamp significantly different from the current time?" -Raymond]
  2. Dmitry says:

    Raymond, how do you generate a lot of those messages? Don't you need a lot windows for that? Meaning next time wouldn't it find previosly generated message? Wouldn't you run out of windows handles before running out of space on the gueue?

    [That's a good question, and I didn't provide all the information necessary to answer it. Answering it will take more than two sentences, so I will toss it onto the topic queue. -Raymond]
  3. Henke37 says:

    First, assume that no shenanigans involving non usual code creating the message by the for other messages normal means will happen.

    Use PeekMessage to create the message, then use filters to ignore it for a while and then clear/change the filters to accept the now old generated message.

  4. Joshua says:

    Since it wasn't obvious the first time, the second computer doesn't need to be part of the interchange process for clock skew to show up. The worst possible moment of time synchronization can cause the same behavior. If your delay timers are not written very carefully, clock skew will blow up your timers.

  5. Joshua Ganes says:

    Raymond, when you say, "We all know that…", who is your target audience" I'm pretty sure that not all of us actually know that.

    Nonetheless, you present a very easy to follow explanation of a reasonably complex topic. I always find these things easier to visualize than to explain in words.

    [I guess they don't teach Win32 the way they used to. The auto-generation of mouse, timer, and paint messages used to be one of the basics of Win32 UI programming that got drilled into you early. -Raymond]
  6. Joker_vD says:

    What is message-filtering useful for, by the way? Except that introducing rarely seen behaviour?

  7. cheong00 says:

    [I guess they don't teach Win32 the way they used to. The auto-generation of mouse, timer, and paint messages used to be one of the basics of Win32 UI programming that got drilled into you early. -Raymond]

    They're barely mentioned (if mentioned they're auto-generated at all), and most doesn't bother to explain the auto-generation machnism in detail.

  8. alegr1 says:

    And this is why you should just use the simplest message loop, if possible. If you have to use some fancy version of it, only do that if you know what you're doing.

Comments are closed.