I want to trigger a Keypress on a Keyboard. On the physical keyboard - so that it has to travel through all the Kernel Filters with that specific keyboard as its source. Is something like that even possible?
I looked at DeviceIOCtrl and IOCTL_KEYBOARD_INSERT_DATA, but there are no docs to be found. Some say, it is not implemented in the driver. Another Source tries to use IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER, but also say, it might not be usable for that kind of stuff.
Are there other options to trigger a keypress on a Keyboard before it travels trough the Kernel Filters?
Why would I even want to do this? Yes this might be an X-Y Question, but its the only solution path I can think of at the moment. I want to intercept keys from a specific keyboard only. There is RawInput, which tells me the Keyboard of a keystroke, but can't intercept it, and there is a LowLevel Keyboard hook that can intercept, but is unable to identify the keyboard. To make matters worse, the RawInput fires after the LowLevel-Hook does, so it is not possible to get the Keyboard before intercepting the keystroke. There is some magic unicorn way to use a global Hook that triggers after RawInput, but it looks like a massive pain to deal with all the edge cases.
There is an API out there to intercept keyboard strokes which is what I opted for. It basically installs a driver that allows to intercept keystrokes and get the keyboard id. But: that ID is kinda abitrary and doesn't follow any rules I can utilize to correlate the integer value to a specific keyboard device. E.g. it changes when replugging the device, does not correlate with the \Device\KeyboardClassX, etc. So the user has to actively press a key everytime the keyboard is reconnected / the app is started so that I can figure out which physical keyboard the id actually represents. And I want to avoid that and automagically get the specific device for a ID. My idea was to trigger a keypress in the keyboard(-controller?) and to catch that using the driver and thus correlating an abitrary ID with the Device.
So if there is a different X for my Y, please feel free to suggest. I'd like to avoid all those edge cases where the Magic Unicorn Way of RawInput and GlobalHook doesn't work reliably. Examples in any major language are fine. Maybe there's no way around the UX-Inconvenience of manually pressing a button at the start of the application to identify keyboards...
I think I found a solution/hack that works for me. I was avoiding the magic unicorn described in the question and stuck to the driver I already had.
The first idea came, when I realized that I'm already able to inject keyboard strokes into Kernel-Land. Just not to a specific keyboard, but a random one. I initially planned to query all available HID-Keyboards using RawInput and then correlate each of those Items with an ID in the driver. But why not do it the other way around? Inject a random device ID with a keystroke and wait for RawInput to trigger the corresponding keyboard.
So that's what I did. I told the driver to inject a keypress for a specific device and waited for the keystroke to reach RawInput. Only two Problems occur:
So I just needed to find a key that doesn't do, like, anything at all. Luckily the driver works with scan codes. This document told me, that there is a range of scan codes (0x55 - 0x7f) that is free for grabs and every manufacturer kinda does something custom in there. So thats where I started scanning and looking for the results in the RawInput-API by blindly firing those ScanCodes into the driver.
Disclaimer: If you want to do this, you better have at least two screens and you make sure that your focused window is not on top of VisualStudio, because your focused window goes instant fullscreen and your keyboard stops working as long as the window is focused... also your unable to go out of fullscreen, even if you get your keyboard control back...
As a matter of fact, there seem to be quite a few free scancodes to choose from. But some of them are used in different countries or don't trigger on all keyboard drivers. I opted for the ScanCode 0x7e, which translates to KeyCode 194 and doesn't seem to be used in any language, but works on all (2) keyboards I had.
Also, this "key" always includes a proper keyboard Handle, so no issues correlating stuff. So as a final implementation, I get the number of connected keyboards using the RawInput-API and cycle through all possible IDs (0-10/20ish to prevent too many lookups/waiting) until I found an ID for every Keyboard. And because the scan code isn't used for anything, the user is not interrupted by sudden caps locks or anything. Also, this needs to be redone everytime a keyboard is replugged, because every plugged in device gets a new ID, even if it already had one earlier.