Some time ago, we learned that the MsgWaitForMultipleObjects
function waits only for new events to arrive. In particular, this means that when MsgWaitForMultipleObjects
tells you that an event has arrived, you need to process all of those events before looping back and calling MsgWaitForMultipleObjects
again, because MsgWaitForMultipleObjects
isn’t going to tell you about those old events.¹
It so happens that the GetQueueStatus
function also manipulates the queue status. Each time you call GetQueueStatus
to check a flag, that flag is marked as no longer “new” in the queue status, in the same way that MsgWaitForMultipleObjects
marks the flag as no longer “new” when it wakes up due to a queue status change.
This behavior of GetQueueStatus
is very surprising, because you don’t expect a function called “Get” to have destructive side effects.
The GetQueueStatus
function returns the queue status in two forms, packed into a single 32-bit integer. The low-order 16 bits contain the new queue status bits (the queue status changes since the last time you asked about them), whereas the high-order 16 bits contain the cumulative queue status bits (all the status bits, both old and new). This is explained in the “Return value” section of the documentation, but it’s something that you may read without fully comprehending the consequences.
Therefore, if you want to know if there is (say) any raw input available, either old or new, you need to write
if (HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT)
And if you want to drain the raw input, you should use a while
loop to keep processing the raw input until the bit goes clear in the high word.
The fact that GetQueueStatus
is a destructive operation is one of those hidden gotchas of the window manager. This sort of atomic “test and clear” operation is common inside the window manager, so they exposed a function that let applications do it too, using the same name that the window manager used internally, so you might say that this function was named while wearing window manager colored glasses: The fact that this is a “test and clear” function is obvious if you work on the window manager, and completely surprising if you don’t.
¹ The MsgWaitForMultipleObjectsEx
function lets you pass the MWMO_
flag to mean, “Also consider old events that haven’t yet been processed.”
Is there a way to post custom coalesceable windows message, like WM_TIMER and WM_PAINT?
The motivation is basically the same as WM_PAINT, "Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.", but with custom tasks.
An imperfect solution is to use MsgWaitForMultipleObjects and SetEvent. But this method is hampered by many internal modal message loops which the application has no control over.
For example, MsgWaitForMultipleObjects has no chance to run when a window/scrollbar is being dragged or a menu is opened, causing the custom task processing to halt.