Paint messages will come in as fast as you let them


There is a class of messages which are generated on demand rather than explicitly posted into a message queue. If you call Get­Message or Peek­Message and the queue is empty, then the window manager will look to see if one of these generated-on-demand messages is due, messages like WM_TIMER, WM_MOUSE­MOVE, and WM_PAINT.

Neil wonders, "In that program that called Invalidate­Rect 100,000 times, how many paint messages were generated?"

The Zen answer to this question is "Yes."

A more practical answer is "As many as you can get."

When somebody calls Invalidate­Rect, the window manager adds the specified rectangle to the window's invalid region (or invalidates the entire client area if no rectangle is provided) and sets a flag that says "Yo, there's painting to be done!" (It's not actually a flag, but you can think of it that way.)

When a message retrieval function finds that there are no incoming sent messages to be dispatched nor any applicable messages in the queue to be retrieved, it looks at these extra flags to see if it should generate a message on the fly. If the "Yo, there's painting to be done!" flag is set on a window that the thread is responsible for, a WM_PAINT message is generated for that window. (Similarly, a WM_TIMER is generated if a timer has elapsed, and a WM_MOUSE­MOVE is generated if the mouse has moved since the last time this thread retrieved a mouse message.)

Therefore, the number of WM_PAINT messages by 100,000 invalidations is not deterministic, but it'll be at least one and may be as high as 100,000. It's basically just a race between the invalidation thread and the paint thread.

Invalidate­Rect
Invalidate­Rect
Get­Message (retrieves WM_PAINT)
WM_PAINT dispatched
Get­Message (waits for a message)
Invalidate­Rect
Get­Message (returns with WM_PAINT)
Invalidate­Rect
WM_PAINT dispatched
Invalidate­Rect
Get­Message (retrieves WM_PAINT)
Invalidate­Rect
Invalidate­Rect
WM_PAINT dispatched
Get­Message (retrieves WM_PAINT)
Invalidate­Rect
WM_PAINT dispatched
Get­Message (retrieves WM_PAINT)
WM_PAINT dispatched
Get­Message (waits for a message)

If the thread doing the painting manages to call Get­Message between each call to Invalidate­Rect, then it will see every invalidation. On the other hand (which is more likely), it only manages to call Get­Message after a few invalidations have taken place, it will see the accumulated invalidation in a single WM_PAINT message.

Now that you understand how generated messages work, you can answer this question which sometimes comes in:

If the user is continuously moving the mouse, how many WM_MOUSE­MOVE messages will I get?

Comments (25)
  1. Gabe says:

    The number of MS_MOUSEMOVE messages depends not only on how often you look for them, but also how often they're generated, making it a much harder question to answer than the WM_PAINT one because we knew how many InvalidateRect calls there were.

  2. Tim says:

    As is the case with all "Schroedinger Messages" the answer to the question on how many you will get is: "Enough to satisfy your curiosity".

  3. Markus says:

    Is there a special term for these kinds of messages? Basically I'd like to know how to find out if a message is handled this way from documentation and/or how to find out which messages are handled this way.

    [I don't know if there is an official term, but generated messages is sometimes used. The three messages which behave this way are documented as WM_PAINT, WM_TIMER, and WM_QUIT. -Raymond]
  4. Joshua says:

    I've had to explain to some engineers about not depending on getting WM_MOUSEMOVE (or even WM_MOUSELEAVE due to something even rarer that Raymond blogged about awhile back). We had a data corruption bug recently that was apparently due to mishandling of these messages.

  5. Leo Davidson says:

    I wish it was possible to have user-defined messages behave in this way. i.e. To flag that a message needs to be sent, without generating a duplicate message if the flag is set and a message is already pending.

    With access to the message loop, you can do it using events instead of messages. Without access to the message loop (the usual case), you can do it, but it's a pain and brings a fair amount of baggage for something that seems like a very common situation.

    (Failure to do it can lead to the message queue overflowing if the event producer can run more frequently than the consumer. Or, more commonly, just wasting time because the consumer ends up doing extra work that isn't actually needed.)

  6. blah says:

    +1 for Leo Davidson's comment. Conflation like this, exposed for user-defined purposes, would be the proper way to handle UI updates from worker threads.

  7. AsmGuru62 says:

    WM_MOUSEMOVE is a tricky message – it sometimes arrives even when mouse is not moved.

    [Yup, we discussed this several years ago. Not sure what your point is. -Raymond]
  8. Me says:

    Maybe AsmGuru62 hasn't read (or doesn't remember) your post from 2003??

    So his point in posting this might essentially be the same as the point of your blog post was back then. To inform others of surprising behavior.

  9. @Leo, blah:

    If you want to conflate your custom message, just use PeekMessage for its code.

  10. Mick says:

    @alegr1: That only works for message loops that you have control over, ie. not for arbitrary threads or modal loops running on your thread(s).

  11. grumpy says:

    If the user is continuously moving the mouse, how many WM_MOUSE­MOVE messages will I get?

    Three pounds of flax. Or five tons. Depends.

  12. Neil says:

    If you don't have too many different messages that you need to conflate, you could use timers.

  13. Stewart says:

    Or you could use WM_PAINT. As an example of this see here http://blogs.msdn.com/…/10249000.aspx which is also from way back in 2003.

  14. Wimon Buchan says:

    "(It's not actually a flag, but you can think of it that way.)" – now I'm slightly curious as to what it is, but I expect it's just an auto-reset event or equivalent. Otherwise, potentially non-obvious but fairly well known behavior.

    To those asking for Windows supporting this style of event, it's not that hard to do yourself, and very hard for windows to do in the way you'd want. Remember that magic has to happen either to combine the messages, which supporting in the general case would be rather difficult. To do it yourself, simply:

    * Define (with the same lifetime scope as the window you're dispatching to) a flag and the data needed (eg, last mouse position for WM_MOUSEMOVE, invalid region for WM_PAINT) and some synchronization (probably just a CritSec) to protect them.

    * After the update if the flag is not set, set it and post the message.

    * On receiving the message process the data (just copy it if it would take long of course) then clear the flag and the data.

    This is far simpler than any wacky callback + handle system you'd need to provide to do anything even slightly complex inside GetMessage() itself (anything more complex than WM_MOUSEMOVE runs out of [WL]PARAM bits), and I can't see any benefit for it to be done there rather than in the target window's WndProc. (I can't see how any implementation of 'lazy' messages could work with a WndProc you don't control to some extent – otherwise you don't know when to clean up the data)

  15. dave says:

    Related question: how many roads must a man walk down?

  16. Dave Bacher says:

    @blah:

    Luke only needed 24 updates a second to confront Vader and the Emperor.

    Kirk only needed 24 updates a second to scream Kaaaaahhhhnnnnnn.

    Using a WM_TIMER at 41ms to update your UI, I think we can agree that whatever your UI is doing, it's not as important as James Tiberius Kirk or Lord Vader.  24 updates a second will be fine for you, too.

    UI updates are costly, and cause several messages, at tens of thousands of CPU cycles per message.  The little red guys and blue guys inside the computer can only be working on a couple of tasks at any given time, and often have to fight great wars for resources like system bus, GPU bus, memory, etc.  The less demanding you are of the little red and blue guys, the more time that they have to deal with other problems on the computer. (For the love of God, haven't you seen Tron?)

    Video games are the exception, but you're not using windows messages for UI updates in a video game, you're always drawing from current state — and generally as quickly as possible.

    Incidentally, it's a commonly known fact that referencing Star Wars and Star Trek during a peer-review increases the odds of the review being a pass by at least one order of magnitude each.

  17. @Stewart:

    Are you suggesting to handle custom flags/messages as part of WM_PAINT? Oops, too bad, the handling gets stalled when the app is minimized or completely obscured, because you don't get those WM_PAINT.

  18. Gabe says:

    alegr1: I would recommend using WM_TIMER for such messages. Instead of setting a flag and then sending a message, just set a timer with a pointer to the function that would handle that flag.

  19. kme says:

    The simplest way to describe this behaviour is probably that the messages in question are level-triggered rather than edge-triggered.

  20. Danny says:

    Hmm… how does Msg­Wait­For­Multiple­Objects know whether one of these "fake" messages would be there? That would maybe explain some curious hangs I got some years ago…

    [The same way that Msg­Wait­For­Multiple­Objects knows that a queued message is present, or that a non-queued message is present. -Raymond]
  21. @Gabe:

    Won't work for cross-thread communication. Can't call SetTimer on other thread's window.

  22. alexcohn says:

    @alegr1: How will ::PostMessage(hWnd, WM_TIMER, …) behave?

  23. @Alex:

    It's generally not a good idea to fake special messages. I'm afraid your GetMessage will just fetch it, without calling a callback function.

    The point Gabe was trying to make is to avoid using of PostMessage or SendMessage for notifying another thread. If you use PostMessage, you better just post a custom code.

  24. For what it's worth, it's trivially easy to write a class for setting a flag (or updating a 64 bit value) and posting a message only once. Judicuous use of interlocked operations does wonders.

Comments are closed.