Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ToUnicode (properly, so it works)

I'm trying to use the ToUnicode function in response to receiving a WM_KEYDOWN notification, which sounds a lot easier than it is.

Actually, ToUnicode should superfluous if one simply uses WM_CHAR, but much to my surprise this actually does not work properly at all! Having used WM_CHAR in no-common-controls-programs for ages, I've just for the first time ever typed a word with a diacritic accent1 only to realize that dead keys don't work at all!
As in, if I type for example ´e, then WM_CHAR is telling me e when it should be telling é (similar for other dead key combinations, such asâ).

ToUnicode seems like the obvious solution according to its documentation -- unwieldy as it is, its MSDN description page states that it does exactly what I need. It does take an awful lot of parameters, but those too seem straighforward.

The first parameter is simply the virtual key code (wParam), and the second one can be obtained via MapVirtualKey.
The third parameter is optional, so not actually needed (that's what "optional" means, isn't it!). Here's the first surprise: If you don't provide the key state, the function simply fails ("no key mapped") for any key you press. Which means an extra call to GetKeyboardState is needed.

That leaves us with this code:

case WM_KEYDOWN:
    BYTE kb[256];
    GetKeyboardState(kb);

    WCHAR uc[5] = {};

    switch(ToUnicode(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC), kb, uc, 4, 0))
    {
    case -1: _putws(L"dead key"); break;
    case  0: _putws(L"no idea!"); break;

    case  1:
    case  2:
    case  3:
    case  4:
    _putws(uc);
    }
...

This outputs the proper Unicode character for every normal character (no big achievement, you already get that from WM_CHAR), but spectacularly fails for dead keys, in the exact same way (one might suspect TranslateMessage uses ToUnicode to produce WM_CHAR).
The shift and alt keys as well as altgr are all honored (so for example typing µ (altgr-m) will give me µ just fine), but the diacritic dead keys (like acute or circumflex) won't work. Which means if you were to try to type some French or Spanish words on my German-layout keyboard, you're without luck.

Is there a way to use this function properly, so it works? Or, alternatively, is there a different function that works properly for dead keys?


1Well, obviously not for the first time, but for the first time in this context.
like image 257
Damon Avatar asked May 05 '14 15:05

Damon


1 Answers

I cannot confirm the symptoms you describe. I used Spy++ to monitor the messages received by Notepad. Then I pressed the dead key ´, followed by an e.

As you can see, the WM_CHAR message is absolutely correct with the Unicode character 233. That represents é.

<00001> 000F0C86 P WM_KEYDOWN nVirtKey:VK_OEM_6 cRepeat:1 ScanCode:0D fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00002> 000F0C86 P WM_DEADCHAR chCharCode:'180' (180) cRepeat:1 ScanCode:0D fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00003> 000F0C86 P WM_KEYUP nVirtKey:VK_OEM_6 cRepeat:1 ScanCode:0D fExtended:0 fAltDown:0 fRepeat:1 fUp:1
<00004> 000F0C86 P WM_KEYDOWN nVirtKey:'E' cRepeat:1 ScanCode:12 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00005> 000F0C86 P WM_CHAR chCharCode:'233' (233) cRepeat:1 ScanCode:12 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00006> 000F0C86 P WM_KEYUP nVirtKey:'E' cRepeat:1 ScanCode:12 fExtended:0 fAltDown:0 fRepeat:1 fUp:1

So WM_CHAR is everything you need if you just want the composed input. Otherwise, you can handle WM_DEADCHAR and process the input manually.

Please read the docs about keyboard input on MSDN.

like image 155
xMRi Avatar answered Oct 26 '22 17:10

xMRi