ToAscii/ToUnicode in a keyboard hook destroys dead keys

五迷三道 提交于 2019-12-17 23:20:15

问题


It seems that if you call ToAscii() or ToUnicode() while in a global WH_KEYBOARD_LL hook, and a dead-key is pressed, it will be 'destroyed'.

For example, say you've configured your input language in Windows as Spanish, and you want to type an accented letter á in a program. Normally, you'd press the single-quote key (the dead key), then the letter "a", and then on the screen an accented á would be displayed, as expected.

But this doesn't work if you call ToAscii() or ToUnicode() in a low-level keyboard hook function. It seems that the dead key is destroyed, and so no accented letter á shows up on screen. Removing a call to the above functions resolves the issue... but unfortunately, I need to be able to call those functions.

I Googled for a while, and while a lot of people seemed to have this issue, no good solution was provided.

Any help would be much appreciated!

EDIT: I'm calling ToAscii() to convert the virtual-key code and scan code received in my LowLevelKeyboardProc hook function into the resulting character that will be displayed on screen for the user.

I tried MapVirtualKey(kbHookData->vkCode, 2), but this isn't as "complete" a function as ToAscii(); for example, if you press Shift + 2, you'll get '2', not '@' (or whatever Shift + 2 will produce for the user's keyboard layout/language).

ToAscii() is perfect... until a dead-key is pressed.

EDIT2: Here's the hook function, with irrelevant info removed:

LRESULT CALLBACK keyboard_LL_hook_func(int code, WPARAM wParam, LPARAM lParam) {

    LPKBDLLHOOKSTRUCT kbHookData = (LPKBDLLHOOKSTRUCT)lParam;
    BYTE keyboard_state[256];

    if (code < 0) {
        return CallNextHookEx(keyHook, code, wParam, lParam);
    }

    WORD wCharacter = 0;

    GetKeyboardState(&keyboard_state);
    int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
                     keyboard_state, &wCharacter, 0);

    /* If ta == -1, a dead-key was pressed. The dead-key will be "destroyed"
     * and you'll no longer be able to create any accented characters. Remove
     * the call to ToAscii() above, and you can then create accented characters. */

    return CallNextHookEx(keyHook, code, wParam, lParam);
}

回答1:


  1. stop using ToAscii() and use ToUncode()
  2. remember that ToUnicode may return you nothing on dead keys - this is why they are called dead keys.
  3. Any key will have a scancode or a virtual key code but not necessary a character.

You shouldn't combine the buttons with characters - assuming that any key/button has a text representation (Unicode) is wrong.

So:

  • for input text use the characters reported by Windows
  • for checking button pressed (ex. games) use scancodes or virtual keys (probably virtual keys are better).
  • for keyboard shortcuts use virtual key codes.



回答2:


Call 'ToAscii' function twice for a correct processing of dead-key, like in:

int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
                 keyboard_state, &wCharacter, 0);
int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
                 keyboard_state, &wCharacter, 0);
If (ta == -1)
 ...



回答3:


Calling the ToAscii or ToUnicode twice is the answer. I found this and converted it for Delphi, and it works!

cnt:=ToUnicode(VirtualKey, KeyStroke, KeyState, chars, 2, 0);
cnt:=ToUnicode(VirtualKey, KeyStroke, KeyState, chars, 2, 0); //yes call it twice



回答4:


Quite an old thread. Unfortunately it didn't contain the answer I was looking for and none of the answers seemed to work properly. I finally solved the problem by checking the MSB of the MapVirtualKey function, before calling ToUnicode / ToAscii. Seems to be working like a charm:

if(!(MapVirtualKey(kbHookData->vkCode, MAPVK_VK_TO_CHAR)>>(sizeof(UINT)*8-1) & 1)) {
    ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode,
        keyboard_state, &wCharacter, 0);
}

Quoting MSDN on the return value of MapVirtualKey, if MAPVK_VK_TO_CHAR is used:

[...] Dead keys (diacritics) are indicated by setting the top bit of the return value. [...]




回答5:


I encountered this issue while creating a key logger in C# and none of the above answers worked for me.

After a deep blog searching, I stumbled across this keyboard listener which handles dead keys perfectly.




回答6:


I copy the vkCode in a queue and do the conversion from another thread

@HOOKPROC
def keyHookKFunc(code,wParam,lParam):
    global gkeyQueue
    gkeyQueue.append((code,wParam,kbd.vkCode))
    return windll.user32.CallNextHookEx(0,code,wParam,lParam)

This has the advantage of not delaying key processing by the os



来源:https://stackoverflow.com/questions/1964614/toascii-tounicode-in-a-keyboard-hook-destroys-dead-keys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!