A customer wanted to know why the input they were simulating with
SendInput
is not being reported by
GetAsyncKeyState
.
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 SendInput
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 Input Queue | ||
↷ | ||||
Dequeue |
Raw Input Thread
| |||
↓ | ||||
Low Level Hooks |
Applications
| |||
↓ | ||||
Update Input State |
Raw Input Thread
| |||
↙ | ↓ | ↘ | ||
App 1 | App 2 | App 3 |
When you call SendInput
,
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
GetAsyncKeyState
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.