Posted messages are processed ahead of input messages, even if they were posted later


Regardless of which interpretation you use, it remains the case that posted messages are processed ahead of input messages. Under the MSDN interpretation, posted messages and input messages all go into the message queue, but posted messages are pulled from the queue before input messages. Under the Raymond interpretation, posted messages and input messages are kept in separate queues, and the message retrieval functions will look first in the posted message queue before looking in the input queue.

Let's run an experiment to see posted messages get processed ahead of input messages. Start with the new scratch program and make these changes:

#include <strsafe.h>

class RootWindow : public Window
{
public:
 virtual LPCTSTR ClassName() { return TEXT("Scratch"); }
 static RootWindow *Create();

 void AppendText(LPCTSTR psz)
 {
    ListBox_SetCurSel(m_hwndChild,
                      ListBox_AddString(m_hwndChild, psz));
 }

 void AppendFormat(LPCTSTR pszFormat, ...)
 {
  va_list ap;
  va_start(ap, pszFormat);
  TCHAR szMsg[256];
  StringCchVPrintf(szMsg, ARRAYSIZE(szMsg), pszFormat, ap);
  AppendText(szMsg);
  va_end(ap);
 }

 void LogMessage(const MSG *pmsg)
 {
   AppendFormat(TEXT("%d\t%04x\t%p\t%p"),
                pmsg->time,
                pmsg->message,
                pmsg->wParam,
                pmsg->lParam);
 }

 ...
};

LRESULT RootWindow::OnCreate()
{
 m_hwndChild = CreateWindow(
      TEXT("listbox"), NULL,
      LBS_HASSTRINGS | LBS_USETABSTOPS |
      WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL,
      0, 0, 0,0, GetHWND(), (HMENU)1, g_hinst, 0);
 return 0;
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                   LPSTR lpCmdLine, int nShowCmd)
{
   ...
   while (GetMessage(&msg, NULL, 0, 0)) {

    switch (msg.message) {
    case WM_KEYDOWN:
     prw->AppendText(TEXT("Sleeping"));
     UpdateWindow(prw->GetHWND());
     Sleep(1000);
     prw->AppendText(TEXT("Posting"));
     PostMessage(prw->GetHWND(), WM_USER, 0, 0);
     break;
    case WM_KEYUP:
    case WM_USER:
     prw->LogMessage(&msg);
     break;
    }

    TranslateMessage(&msg);
    DispatchMessage(&msg);
   ...
}

This program creates a list box so we can display some output. In the message loop, it sniffs at all the queued messages and does the following:

  • If the message is WM_KEY­UP or WM_USER, then it logs the message timestamp and some parameters.

  • If the message is WM_KEY­DOWN, then it sleeps without processing messages for one second, and then posts a WM_USER message to the main window (which ignores it).

Run this program, and then tap the shift key.

The window gets a WM_KEY­DOWN for the shift key. It sleeps for one second (plenty of time for you to release the shift key), and then posts a WM_USER message.

The WM_USER and WM_KEY­UP messages arrive, and observe via the log window that they arrive out of order. WM_USER message arrived first!

That's because of the rule that says that posted messages are processed ahead of input messages. (Depending on how you want to look at it, you might say that posted messages are "called out for preferential treatment" in the queue, or you might say that posted messages are placed in a different queue from input messages, and the posted message queue has higher priority.)

Observe also that the timestamp on the WM_USER message is greater than the timestamp on the WM_KEY­UP message, because the key went up before the WM_USER message was posted. Time has gone backward.

Make the following change to our program: Change the message we post from WM_USER to WM_KEY­UP:

      PostMessage(hwnd, WM_KEYUP, 0, 0);

Run the program again, and again tap the shift key. Observe that the posted WM_KEY­UP message is processed ahead of the WM_KEY­UP input message. (You can see the difference because we posted the WM_KEY­UP message with wParam and lParam both zero, whereas the WM_KEY­UP input message has information in those parameters.)

This little demonstration also reinforces some other things we already knew. For example, it once again shows that the input manager does not wiretap your posted messages. If you post a WM_KEY­UP message, it is treated like a posted message not an input message. We saw earlier that posting a keyboard message does not update internal input states. The keyboard shift states are not updated to match your prank call message. If somebody calls Get­Queue­Status, they will not be told that there is input waiting. It will not wake a Msg­Wait­For­Multiple­Objects function that is waiting for QS_INPUT. And as we saw here today, the message gets processed out of order.

Comments (9)
  1. Joshua Ganes says:

    I like your dual-queue explanation better than the MSDN pseudo-priority-queue explanation. I find it simpler to conceptualize and I find that it is more intuitive when it comes to posted messages disguised as input messages. Otherwise I would think that the false input messages might follow the same rules as true input messages.

  2. Brian_EE says:

    "Time has gone backward."

    Either that or these are indications to Microsoft Research's time machine that Raymond hints is being developed. Where are the conspiracy theorists when you need them?

  3. D-Coder says:

    "Where are the conspiracy theorists when you need them?"

    Go to CNN.com and read the comments.  At least an aleph-zero of them.

  4. Joshua says:

    I found this piece of library code we obtained from a third party who does not deserve to be named who was doing SendMessage(…, WM_MOUSEDOWN, …) to a system control. I was able to slightly reduce the undocumented and unspecified behavior by following it with WM_MOUSEUP. It's still a bad idea I couldn't easily remove. (Trying to defer-create textbox until it is needed sounds like a good idea until you realize it can't get the WM_MOUSEDOWN that caused it to exist.)

  5. This is missing the "takeway" or "learnings" :)

    Do NOT post input messages, it will not do what you might think it does.

  6. Jonathan says:

    Out of curiosity: How is the message queue actually implemented: Preferential queue, or 2 separate queues? My money is on the latter.

  7. Alois Kraus says:

    You said that for simulating keyboard input we should use SendInput. You also said there are still many issues left with this solution. Is there any guidance available which documents the other issues with proposed workarounds? I am sure there are millions UI interaction tests running each day and thousands of them are failing sporadically. It is a very tedious undertaking to find issues others had as well. UI automation tests are still necessary even with all these nice isolated unit tests with mocked interfaces and behaviour testing.

  8. Neil says:

    As I recall, old versions of the Flash plugin used to post itself messages all over the shop, which was really annoying because Netscape 7 wanted to process input messages by preference. They ended up peeking keyboard, ime and mouse messages in order to subvert the priority system: mxr.mozilla.org/…/nsAppShell.cpp although Firefox 23 (!) will revert to mostly the default message order (except apparently for certain mouse wheel messages).

  9. sirin says:

    Is there a way to post a low priority message with the same priority as an input message?

    [All posted messages are treated like posted messages. -Raymond]

Comments are closed.

Skip to main content