ToAscii/ToUnicode в клавиатурном крючке уничтожает мертвые ключи

Кажется, что если вы вызываете ToAscii() или ToUnicode() в то время как в глобальном крюке WH_KEYBOARD_LL и нажата мертвая клавиша, он будет "уничтожен".

Например, скажем, что вы настроили язык ввода в Windows как испанский, и вы хотите ввести буквенную букву á в программе. Обычно вы нажимаете клавишу одиночной кавычки (мертвая клавиша), затем буква "a", а затем на экране будет отображаться акцентированная á, как и ожидалось.

Но это не сработает, если вы вызываете ToAscii() или ToUnicode() в низкоуровневую функцию перехвата клавиатуры. Кажется, что мертвый ключ уничтожен, и поэтому на экране не появляется акцентированная буква á. Удаление вызова вышеуказанных функций устраняет проблему... но, к сожалению, мне нужно иметь возможность вызывать эти функции.

I Googled какое-то время, и, хотя у многих людей, похоже, была эта проблема, не было найдено хорошего решения.

Любая помощь будет очень признательна!

EDIT: Я вызываю ToAscii() для преобразования виртуального key code и кода сканирования, полученного в моем LowLevelKeyboardProc в результирующий символ, который будет отображаться на экране для пользователя.

Я пробовал MapVirtualKey(kbHookData->vkCode, 2), но это не как "полная" функция как ToAscii(); например, если вы нажмете Shift + 2, вы получите "2", а не "@" (или что бы Shift + 2 произвел для раскладки/языка клавиатуры пользователя).

ToAscii() идеально... до тех пор, пока не будет нажата мертвая клавиша.

EDIT2: Здесь функция hook с ненужной информацией удалена:

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

  • прекратить использование ToAscii() и использовать ToUncode()
  • помните, что ToUnicode не может вернуть вам ничего на мертвые ключи - вот почему они называются мертвыми ключами.
  • Любая клавиша будет иметь scancode или виртуальный key code, но не обязательно символ.

Нельзя комбинировать кнопки с символами - при условии, что любая клавиша/кнопка имеет текстовое представление (Unicode).

Итак:

  • для ввода текста используйте символы, о которых сообщает Windows
  • для проверки нажатой кнопки (например, игр) используются scancodes или виртуальные ключи (возможно, виртуальные ключи лучше).
  • для сочетаний клавиш используйте коды виртуальных клавиш.

Ответ 2

Дважды вызовите функцию "ToAscii" для правильной обработки мертвой клавиши, например:

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

Вызов ToAscii или ToUnicode дважды - это ответ. Я нашел это и преобразовал его в Delphi, и он работает!

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

Ответ 4

Довольно старая нить. К сожалению, он не содержал ответа, который я искал, и ни один из ответов не работал должным образом. Я, наконец, решил проблему, проверив MSB функции MapVirtualKey, прежде чем вызывать ToUnicode/ToAscii. Кажется, он работает как шарм:

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

Указание MSDN на возвращаемое значение MapVirtualKey, если используется MAPVK_VK_TO_CHAR:

[...] Мертвые клавиши (диакритики) обозначаются установкой верхнего бита возвращаемого значения. [...]

Ответ 5

Я копирую vkCode в очередь и делаю преобразование из другого потока

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

Это имеет то преимущество, что не задерживает обработку ключа с помощью os