Dmitry wondered how it's possible for a lot of auto-generated messages to pile up in the message queue. I remarked, "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."
One of my colleagues wrote to me and said,
"Hey, could you bump up the priority of that topic?
I happen to have a bug where COM calls are failing because
the message queue is full.
I wrote some diagnostic code to drain the message queue to see
what was in it, and it was full of
There were 53 timers running at 16ms each,
and the UI thread stopped processing messages for 9 seconds."
Back when I explained how asynchronous input worked, I didn't talk about where auto-generated messages came from.
If a request for a message is about to say, "Nope, no matching messages," the window manager makes one last check: "Is there an auto-generated message that could satisfy this request?" If so, then it generates the message, and hey look, there's a message!
The catch is that auto-generated messages are grouped together.
For example, if you ask for any kind of mouse message,
and there is an auto-generated
then the window manager will generate a
WM_ and then check if that matches
the filter you provided.
The auto-generation is done this way so that message ordering is preserved
within a group.
You wouldn't want a mouse-up to be generated before the corresponding
The message groups can be see in functions like
PM_ flags to
Okay, now we're getting closer to seeing how auto-generated messages can pile up: If you are filtering for a message, and there is an auto-generated message from the same group, but which doesn't match your filter, then the window manager will auto-generate the message, and then go back and re-run the "Find a message" code, which sees the auto-generated message but says, "Nope, I'm not interested in that message."
Another piece of the puzzle is understanding the timer group.
There are two messages in the timer group.
One is your friend and mine,
The other is an undocumented internal message known as
This is an alternate universe of timers used by the system
to manage system things,
like the animated concentric circles in the
deciding when to time out the system tooltips
(like the one that appears when you hover over the × button),
driving autorepeat when you click on the scroll bar,
and blinking the caret in an edit control.
The final piece of the puzzle is the COM modal message loop. This is the message loop used by COM when you call a method on an STA that needs to be marshaled. COM notifies the destination thread that it needs to run some code, and then it enters a modal message loop waiting for the destination thread to reply, "Okay, I'm done. Here's the answer."
The COM message loop is a complicated beast,
most likely the result of over twenty years of evolution
rather than having been designed that way from the beginning.
One of the things that it does is peek
Another thing that it does is dispatch timer messages,
provided you passed the
Okay, here comes the wild ride.
COM wants to process
It therefore does a
If there is a
WM_ message due,
then the window manager generates the
WM_ message on the fly,
puts it in the queue,
PeekMessage function returns it.
That's the good case.
Another good case is that there is neither a
WM_ message due.
In that case, the window manager generates nothing,
PeekMessage function returns
"Sorry, I didn't find anything."
The bad case is where there is no
but there is a
WM_ message due.
In that case, the window manager generates the
on the fly and puts it in the queue.
PeekMessage function ignores
because it's interested only in
WM_ message got
generated and dumped into the queue.
Every time a
WM_ comes due,
WM_ message gets generated
and added to the queue.
Eventually, your queue fills up with
My colleague replied, "Thanks for the explanation. Of course, it's COM, the Bermuda triangle of Win32!"