The dangers of filtering window messages

The GetMessage and PeekMessage functions allow you to pass a filter, restricting the window handle or range of messages that the function will retrieve from the message queue. While it's okay to use these filters, make sure you eventually get around to making an unfiltered call so that any straggling messages can come through.

A common mistake is to use a window-filtered GetMessage in your message loop. In our scratch program, a window-filtered GetMessage would look like this:

        while (GetMessage(&msg, hwnd, 0, 0)) { // Wrong!

Even though the program creates but one window, this program is nevertheless incorrect.

"How can this be?" you ask. "My program has only one window. Why would there possibly be any messages for any other windows? The filter, while redundant, isn't harmful, is it?"

Many system services create windows on your behalf. For example, if input method editing is enabled, the method editor may create helper windows to assist in character input. If you initialize COM, then COM may decide to create a helper window to assist in inter-thread marshalling. If you use only a filtered GetMessage, then messages destined for these helper windows will never be retrieved, and you will be left scratching your head wondering why your program occasionally hangs when it tries to perform a drag/drop operation, for example.

Moral of the story: Make sure your message loop eventually performs an unfiltered message retrieval so that these services can operate properly.

Comments (21)
  1. Mike says:

    Useful reminder! I’ve just been through my code to check I never over-filter…

    On a tangentially related subject, is there any rational way to call a PeekMessage function in Windows? The function PeekMessage is actually PeekMessageAndSilentlyPerformAnyIncomingSendMessagesThatAreLyingAround, which can be disastrous if all you really want to do is peek. I’ve got seriously caught out by this in the past, and I can’t be the only one.

  2. Tom Seddon says:

    Does the PM_NOREMOVE flag not do that?

  3. Waleri says:

    I just wonder, what is the purpose of this filtering ability in a first place?

  4. Wez Furlong says:

    Not quite on topic, but close-enough…

    Having played with QueueUserAPC and WSASend() and WSARecv() with completion functions recently, it seems a bit overly verbose to have to use MsgWaitForMultipleObjects() and a host of flags to implement an alertable GetMessage() loop, to have my completion routines run on my main gui thread. Just wondering why things are this way, or if, perhaps, I’m doing something wrong.

  5. Steven says:

    Filtering also causes some weird results if you have a dropdown combobox on your window. When the drop list is opened, everything seems to stall since the list gets no messages.

  6. Raymond Chen says:

    Wez Furlong: 1. Compatibility – programs would have code running at times they weren’t expecting. 2. Compatibility – what should GetMessage return when it was woken alertably? You can’t return FALSE – that will cause the loop to exit. You can’t return TRUE – that will cause the app to try to dispatch a nonexistent message.

  7. A says:

    Mike: Specify PM_QS_POSTMESSAGE or another PM_QS_* flag in your PeekMessage call without PM_QS_SENDMESSAGE.

    (Note: These flags are only available on 98/2000+.)

  8. Mike says:

    A: thanks! I missed that in the documentation somehow.

  9. vj says:

    while (GetMessage(…))

    GetMessage can also return -1 indicating a failure.

  10. Raymond Chen says:

    Please use the suggestion box for topic suggestions. Otherwise I’ll lose track.

  11. James says:

    Also, GetMessage returns more than just TRUE and FALSE, so using it like that in the condition of a while loop seems to be showing the kind of disregard for API specification that Raymond is usually so careful to publicly abhor.

  12. Some Guy says:

    Done, but now I fear it’ll never be answered. Oh well :)

  13. Raymond Chen says:

    I can’t answer everything.

  14. Mike says:

    Tom, I didn’t explain myself very well – I *wanted* PM_REMOVE – the point was that I did want to remove selected messages but specifically not have all the incoming SendMessages processed.

    asdf – thanks for the GetQueueStatus reference: it seems to say that what I want is impossible.

  15. RJ says:

    Some Guy – This page probably answers your question:

    "If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty"

  16. Some Guy says:

    Slightly offtopic but one thing I’d be interested for you to blog on is how some apps apparently get away with missing out a call to BeginPaint/EndPaint in their WM_PAINT handling code.

    Some open source win32 apps do this and yet don’t hang, but if I just do:

    case WM_PAINT: return 0;

    then Windows 98 loops forever inside CreateWindow.

    Are there some appcompat hacks in this area? It feels like some magic API calls do a BeginPaint for you if you forget, or do *something* so that Windows is happy.

  17. A says:

    Mike: One more thing that just occurred to me: in addition to a PM_QS_* flag you also need the PM_NOYIELD flag — evidently the act of "yielding" involves processing sent messages.

  18. Matt says:

    Call me dumb, but I don’t understand why you would use a message filter in the first place. I take it you would use it to prioritize messages or something? What is an example?

  19. Wez Furlong says:

    Raymond: it’s my understanding that GetMessage() does other things internally while waiting for messages; it seems like it could also handle kicking off APCs without returning to the caller. So I guess the main reason is your #1; compatibility with code that doesn’t expect things to happen that way. Thanks :)

  20. Igor Grebnev says:

    while (GetMessage(&msg, hwnd, 0, 0)) – is also wrong for other reason – your applicaion will never exit. WM_QUIT is posted to main thread with NULL for windows handle ( PostQuitMessage ). In this case GetMessage never retrieves WM_QUIT and application hangs. I have learned it in hard way teaching class of people Windows programming. My test application never exited and in Windows 3.1 blocked whole OS. I honestly said that I do not know the problem and found it only for next meeting.

    I always knew that teaching brings a lot of new knowledge and this is good example.

Comments are closed.