Suppose you want to distinguish between dismissing a dialog by pressing ESC and dismissing a dialog by clicking the Close button. One suggestion I saw was to call GetÂAsyncÂKeyÂState( to check whether the ESC is down.
In general, any time you see GetÂAsyncÂKeyÂState, you should be suspicious, since GetÂAsyncÂKeyÂState checks the state of the keyboard at the moment it is called, which might not be relevant to your window if it asynchronously lost keyboard focus, and which (from the point of view of your program) might even be from the future.
Recall that the system maintains two types of keyboard states. One is the synchronous keyboard state, which represents the state of the keyboard as far as your program is aware. If your program received a WM_ for the space bar, then GetÂKeyÂState will report that the space bar is pressed. Even if the user releases the space bar, GetÂKeyÂState will continue to report that the space bar is pressed until the program receives a WM_ for the space bar.
The idea here is that your program is processing an input stream, and due to the nature of multitasking and the physics of time, it’s possible that your program is catching up to input that actually occurred some time ago. For example, maybe the user typed ahead into the program while it was unresponsive, and now that the program has become responsive again, it is playing catch-up with all the typing that occurred 30 seconds ago.
If the program receives, say, a press of the F2 key and wants to know whether to do the Ctrl+F2 hotkey, it doesn’t know want to know whether the Ctrl key is down at the moment it is processing its input backlog. It wants to know whether the Ctrl key was down at the time the F2 was pressed.
| Time | Event |
|---|---|
| 1 | User presses Ctrl |
| 2 | User presses F2 |
| 3 | User releases F2 |
| 4 | User releases Ctrl |
| 5 | Program receives Ctrl down |
| 6 | Program receives F2 down |
| 7 | Program uses GetÂAsyncÂKeyÂState to ask if Ctrl is downAnswer: No Program does F2 action instead of Ctrl+F2 |
When the program asks via GetÂAsyncÂKeyÂState whether the Ctrl key is down, the answer is “No, it’s not down. It was released at some future point in time you haven’t learned about yet.” And so the program does its F2 action instead of Ctrl+F2, and you get a bug report that goes like “When the program is under heavy load, the Ctrl+F2 hotkey doesn’t work.”
Suppose your program wants to discard changes when the user dismisses the dialog with ESC but wants to save changes when the user dismisses the dialog with the Close button. Checking the asynchronous state of the ESC key will tell you whether the ESC is down right now, but not whether the ESC was down at the time the system generated the IDCANCEL. You’re going to get bugs like, “When the program is under heavy load, pressing ESC to dismiss the dialog sometimes saves the changes instead of discarding them.”
Of course, the bug report probably isn’t going to be so kind as to mention that the program was under heavy load or that the ESC accidentally saved the changes. It’ll probably just say “Sometimes I see changes that I’m sure I had discarded.” And you’ll have to figure out what the necessary conditions are for that bug to manifest itself.
Bonus chatter: I don’t know why people love to use GetÂAsyncÂKeyÂState so much. It has a longer, more cumbersome name than the largely-ignored GetÂKeyÂState function. Maybe people think that the longer, more cumbersome name means that it’s somehow “more fancy”.
I like the idea of fancy functions and will add them in my next release. No actual functionality differences for them, just a change to the CSS for the documentation to them shiny and sparkly. 😀
I think other Windows applications suffer the same issue like Event Log.
Suppose you want to distinguish between dismissing a dialog by clicking the Close button and dismissing a dialog by clicking the other Close button displayed when you hover the mouse over the Windows Explorer taskbar window thumbnail preview when the window is inactive?
A window created using the PropertySheet function (for example the file properties dialog or any other property sheet dialog) can't be dismissed or closed in this case. If you hover the mouse over the taskbar window preview and click the close button just above the window preview when the window is inactive; the window doesn't close and...
Some day, someone will write `co_await GetÂAsyncÂKeyÂState(key);` to suspend until that key is pressed, and then be confused about the error messages due to not reading documentation.
Nope, because the co_await’able function would be named GetKeyStateAsync with the Async at the end, not the middle. At the end, it modifies the function behavior. In the middle, it modifies “key state”. (Also, it would be named WaitForKeyAsync.)
For me the difference is not so much whether the keyboard state is obtained asynchronously or not, but rather if the app has the keyboard focus or not.
Let's say I want to provide a startup "safe-launch" option that resets the recently used main window coords to say a 10,10,640x480 window (for example you usually have 2 screens but one of them just broke down and the app window is not visible when you launch it.)
Usually this is done by detecting a left- or right-shift being held down when the app is launched. I've found this detection to be much more...
It can still apply even if you have keyboard focus. You might be working through a typeahead backlog. The GetKeyState tells you the state of the keyboard based on the input you have processed (or are currently processing). The GetAsyncKeyState lets you peek into the future and see what the final state is before you get there.
Checking for keys pressed at launch is one of the few cases I can think of where GetAsyncKeyState is useful, because you want to know the keyboard state before your window can even receive input at all.
"Suppose your program wants to discard changes when the user dismisses the dialog with ESC but wants to save changes when the user dismisses the dialog with the Close button."
That is valid for exploring the current matter, but from the point of view of an user, it can be infuriating. If I close a dialog, be it pressing Esc, clicking on Cancel or closing the window, I expect no changes to be made (except, maybe, those done in nested dialogs where I clicked OK). I don't know if it's on the Windows user interface guidelines, but almost all applications behave...
I agree that it’s not the best design, but I was trying to motivate the scenario. Maybe “Display a confirmation prompt if they click the X [because users don’t realize that X is a Cancel], but suppress it if they explicitly hit ESC or click Cancel.”
I think the only times I’ve used Get*ASYNC*KeyState was to distinguish between left and right modifiers, as GetKeyState only tells you “modifier pressed” not which one. I used it to allow my code to stop the RightAlt pretending to also press Ctrl at the same time.
Tho’ you’ve prompted me to check my code, muscle memory may have deposited some GetAsyncs somewhere.
You sure you’re not mixing up Right Alt with AltGr?
I suspect that if you described the differences between those two methods, and then asked which one was GetÂAsyncÂKeyÂState and which was GetÂKeyÂState without any background knowledge here I would have swapped them. It's odd to think that the "Async" one reflects the current state and the "non-async" one describes the past state, as we tend to think of async as being related to longer running things. Likely the only way I would remember that is that the "Sync" one is "Synchronized" with the events, but that feels like a stretch because it's not called "GetSyncKeyState".