How do I get the @!#$% name of the keyboard?

by Michael S. Kaplan, published on 2004/12/05 02:02 -05:00, original URI:

I hinted at some of the problems with the HKL concept in my list of keyboarding terms. But I do have several ex-girlfriends who have in common the fact that they all think I suck at the subtle thing and should stop trying to do it. So here follows the formal discussion of the problems with terminology and meaning regarding the HKL in Windows.

Much of what follows is stuff I learned during the time that MSKLC was being developed -- I had to dig down deep into keyboard layouts, and I got the chance to see how complicated some parts of it are (not to mention how convoluted).

The problem starts as a terminology thing. HKL [obviously] originally meant "handle to a keyboard layout" but in recognition of the fact that there are all sorts of things beyond keyboards, they are now defined vaguely in the Platform SDK as the input locale identifier (ignore the fact that they are not identifiers and they are certainly not locales). Also, according to the GetKeyboardLayout topic (and I think this is a longstanding definition), the "...low word contains a language identifier for the input language and the high word contains a device handle for the physical layout of the keyboard.

Obviously this can be wrong since on some platforms that handle is 64 bits and what they really mean is the high word of the low dword. It is also kind of misleading anyway since if I have a single US-English keyboard loaded and I call GetKeyboardLayout then it will return 0x409, meaning that the high word is 0x0000 -- an invalid value for a handle by almost anyone's definition.

Then we get to usefulness problems. Notice that there is no real information about the keyboard layout itself there? There is language information, but its not the language of the keyboard layout itself, its the language that the user put it under after choosing the language/layout pair in the "add keyboard layout" UI.

So what do I do if I want to find out what the layout actually is? You know, say I wanted to (for example) load it again some day for the user, or even just get the name they see when they added the keyboard? Well, if its the currently selected layout in a thread then you can call the GetKeyboardLayoutName API, which claims it "retrieves the name of the active input locale identifier (formerly called the keyboard layout). Only it doesn't. It returns an 8-character string that contains the KLID, which is not really much of a name unless you speak a language that has words like "00030409" in its vocubulary.

The rest of the people in the world (including me) want the actual name and expect to get back "United States-Dvorak for left hand". To get that, I now have to go to HKLM\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00030409 and look for the "Layout Text" value. Or maybe I have an MUI copy of Windows and have changed my language and am on Windows >= XP that string will not be translated appropriately, so I have to look at the "Layout Display Name" value, which has the helpful string "@%SystemRoot%\system32\input.dll,-5027" in it -- which thankfully can be sent to SHLWAPI's SHLoadIndirectString to get the name. In the back of my mind I have to remember that its all subject to change since none of it is documented.

Now what about if its not the currently selected layout? Well, in that case, yuck. Because there is not currently a GetKeyboardLayoutNameEx that takes an HKL. Given the indirect nature of these handle-esque things, I would have to spend a bunch of time in the undocumented values under the HKCU\Keyboard Layout\Preload and HKCU\Keyboard Layout\Substitutes keys. Again, in the back of my mind I have to remember that its all subject to change since none of it is documented. Which makes it even less appealing to figure out the Preload and Substitutes mappings (in case I were enough of a masochist that I found it appealing up until that point)....

Now its a little expensive, but I can't help wondering whether it would be easier to just create a new thread and switch the layout there, after which a call to GetKeyboardLayoutName would work just fine. No one would get any special notifications, and there is no dependency on undocumented registry cruft.

Or maybe I should send a plea for GetKeyboardLayoutNameEx somewhere? :-)

# Uwe Keim on 5 Dec 2004 2:41 AM:

Functions ending on "...Ex()" always seem strange to me. Kind of "botchiness" (if my german-english translator gave me the correct word for this... :-)

# michkap on 5 Dec 2004 4:08 AM:

It is interesting, but the reasons seem different for each area of the Win32 API where they have happened. In NLS, the -Ex versions of functions add stuff that was not thought of. This is of course the most common usage, and it kind of sloppy but better than the alterntives (whole new API names for extensions, or simply not shipping new functionality).

In the keyboarding section of USER, on the other hand, every -Ex version is the same as the non-Ex version except it adds an HKL parameter to extend the API beyond the current user-selected HKL. In their case, I see a consistency that is kind of comforting....

# . on 5 Dec 2004 9:34 AM:

Ahh the beauty of overloading or versioning, why bother with flat C and so on with all these problems, flat C and C++ is NOT an APPLICATION language and never was designed to be such. ITs a system language. You chose the wrong language if you use it as an app language.

# michkap on 5 Dec 2004 3:13 PM:

Ah, this is an interesting thought -- I started to answer and then I realized that the topic deserves its very own posting. Look for it some time this week! :-)

# Eli Golovinsky on 9 Dec 2007 7:37 PM:

I found this post looking for a way to generate a list of installed keyboard layouts like the one that the Language Bar shows when you click on it. After much digging, I found out that it doesn't display the name of the keyboard, but the name of the currently chosen language.

That is apparently much easier to discover, although not less contrived.

   char name[MAXLEN];
   HKL hkls[256];
   count = GetKeyboardLayoutList(MAX_LAYOUTS, hkls);

   for(int i = 0; i < info->count; i++)
       LANGID language = (LANGID)(((UINT)hkls[i]) & 0x0000FFFF); // bottom 16 bit of HKL is LANGID
       LCID locale = MAKELCID(language, SORT_DEFAULT);

       GetLocaleInfo(locale, LOCALE_SLANGUAGE, name, MAXLEN);

Hope this helps someone someday.

# Michael S. Kaplan on 9 Dec 2007 9:02 PM:

Hi Eli,

Not sure I understand here -- the topic is about getting keyboard names, not locale names (which is what LOCALE_SLANGUAGE will return).

Though the locale names are fine too,  what will you do when more than one is under the same locale (much more common than the same keyboard under two different locales!)....

# Michael S. Kaplan on 9 Dec 2007 9:03 PM:

And if course it won't work so well for custom locales and custom keyboard languages....

referenced by

2008/09/29 What a tangled web we weave when a KLID from an HKL we must receive

go to newer or older post, or back to index or month or day