by Michael S. Kaplan, published on 2008/01/02 10:16 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2008/01/02/6951081.aspx
A while back, Mark-Andre sent me the following message:
I'm a young programmer interested in digging the subject you seem to be a specialist in. I'm currently making a keylogger, just for the fun of it, to see how it can be done (I just want to make clear I have no malicious intentions, otherwise I would have downloaded a complete one from the internet). Here's the current source code I have:
The most relevant part of code is the keyLog() function. As you can see, I'm catching key presses with the GetAsyncKeyState() function and then converting them to their corresponding characters with the ToUnicode() function. It works fine, but it does not handle dead keys at all. When I press a dead key, the ToUnicode() function returns 2 instead of -1 (that's not how it should behave according to msdn). While this is not a very important issue for people using English on their computer, it becomes annoying here in Quebec for people using French. There are many accented letters in French that are typed using a dead key, so keylogging without handling them makes the logs harder to read. So far, I'm getting the spacing version of the accent instead. Not _so_ bad.
I've googled a lot about this, but there seem to be no easy solution. I've found that the keyboard layout information is stored in those kbd*.dll. As you said on your blog, these DLLs contain the KbdLayerDescriptor() function. You also said it was defined in kdb.h, available in the Windows DDK. I'm afraid using this function will require admin privileges, right, as it is at the driver level? Also, information about this function seems to lack on msdn.
Basically, all I'm looking for is a way to extract detailed information about the current keyboard layout from those DLLs, so that I can handle the dead keys by myself. Any idea on how I could parse these DLL to get information on how to combine dead keys with keys to get the resulting characters right?
Now the problem here is one that I kind of explained in my series on interrogate keyboard layouts entitled Getting all you can out of a keyboard layout (that link points to the last post in the series, which itself has links to the other parts).
While it is possible to call the keyboard layout DLLs directly, there are a lot of worries here (such as architectural differences between 64-bit, 32-bit, and WoW64 (which is 32-bit running on 64-bit).
Mark-Andre was able to get a version of this code working based on the information in kbd.h:
I have made a function to replace (and improve) the ToUnicode() function. It finds the current keyboard layout and its DLL, and then loads it. It uses the information returned by KbdLayerDescriptor in order to get the information related to the keyboard layout and then uses it in order to make the conversion between the keys typed and the wchars that appear. It even supports dead chars, something that ToUnicode() doesn't do (and even messes up).
He also provided the code he used, which I am not providing here.
You see, this kind of solution would not work across all of those architectures, so on the whole I kind of prefer to use the code based on the documented functionality in user32.dll rather than the "only documented in a DDK header file" functionality being relied on here -- especially given the changes that have even occasionally happened between versions with kbd.h? They just don't make the same kinds of compatibility guarantees.
The dead key issue is a problem that is very well-documented, and working with those documented ways is the best way to solve the problem, overall....
This post brought to you by ⌦ (U+2326, aka ERASE TO THE RIGHT, fka DELETE TO THE RIGHT)
Mihai on 3 Jan 2008 5:26 PM:
Why hook WM_SYSKEYDOWN, WM_KEYDOWN and not directly WM_CHAR, WM_UNICHAR, WM_IME_CHAR?
Michael S. Kaplan on 3 Jan 2008 7:44 PM:
At the driver level, that would be all one has to work with (the character stuff is too late to save things like dead key info)....
go to newer or older post, or back to index or month or day