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