When something gets added to a queue, it takes time for it to come out the front of the queue

A customer wanted to know why the input they were simulating with Send­Input is not being reported by Get­Async­Key­State. Isn't that supposed to reflect the instantaneous keyboard state? I just pushed the key down (or at least simulated it), but when I ask if the key is down, I'm told "Nope." What's the deal?

INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wVk = 'A';
input.ki.wScan = 'A';
input.ki.dwFlags = 0; // key down
SendInput(1, &input, sizeof(INPUT));

assert(GetAsyncKeyState('A') < 0);

The Send­Input call simulates pressing the A key, and the code immediately checks whether the key is down.

But sometimes the assertion fires. How can that be?

Because you're asking the question before the window manager has fully processed the input. Here's a little diagram.

Mouse Keyboard Hardware
SendInput Hardware

Raw Input Thread

Raw Input Thread
App 1 App 2 App 3

When you call Send­Input, you're putting input packets into the system hardware input queue. (Note: Not the official term. That's just what I'm calling it today.) This is the same input queue that the hardware device driver stack uses when physical devices report events.

The message goes into the hardware input queue, where the Raw Input Thread picks them up. The Raw Input Thread runs at high priority, so it's probably going to pick it up really quickly, but on a multi-core machine, your code can keep running while the second core runs the Raw Input Thread. And the Raw Input thread has some stuff it needs to do once it dequeues the event. If there are low-level input hooks, it has to call each of those hooks to see if any of them want to reject the input. (And those hooks can take who-knows-how-long to decide.) Only after all the low-level hooks sign off on the input is the Raw Input Thread allowed to modify the input state and cause Get­Async­Key­State to report that the key is down.

And if you manage to look before all this happens, your code will see that the key isn't down yet.

It's like dropping a letter in the mailbox and then calling somebody to say, "Did you get my letter yet?" Okay, the Raw Input Thread is faster than the Postal Service, but you still have to give it a chance to get the message, query each of the low-level input hooks, decide who the message should be delivered to, and put it in their message queue.

Comments (9)
  1. Henke37 says:

    I think the drawing is faulty. A line is missing, another line is too long and an arrow is pointing the wrong way.

    [It seems Firefox gets the borders of the "Hardware Input Queue" box wrong; Chrome and Opera get the borders of the "SendInput" box wrong. So fine, I got rid of the borders entirely. -Raymond]
  2. DWalker says:

    Nice chart!  I like it.

  3. internalsio says:

    This is rather handy! Thanks for constructing the visual :D

  4. Joshua says:

    [It seems Firefox gets the borders of the "Hardware Input Queue" box wrong; Chrome and Opera get the borders of the "SendInput" box wrong. So fine, I got rid of the borders entirely. -Raymond]

    That's a good case for filing a bug with the respective browsers if I ever saw one.

  5. Myria says:

    I just wish that Windows would consistently send key-up messages for the same virtual key codes that it send key-down messages for.

  6. Joshua Ganes says:

    I like your analogy substituting the postal service for a message queue. I find I use the same thought process when I run into trouble. It prevents me from the dangerous thought pattern of imagining everything a computer does to be atomic and instantaneous and helps me think through race condition bugs.

  7. Anon says:




    Will Not Fix

    We have no interest in not further damaging the Internet to forward our own idiotic design decisions at this time.


  8. DWalker says:

    @Anon:  No interest in not further damaging?

  9. John Doe says:

    That diagram explains the input pipeline better than the whole MSDN put together.

    @Raymond, you ought to have a specific tag for this kind of posts. I know you usually say this is not official (or that's the expected disclaimer, somewhere, somehow), but the diagram explains a whole lot!

Comments are closed.