Обработка ввода клавиатуры в win32, WM_CHAR или WM_KEYDOWN/WM_KEYUP? - программирование
Подтвердить что ты не робот

Обработка ввода клавиатуры в win32, WM_CHAR или WM_KEYDOWN/WM_KEYUP?

Итак, в программе текстового редактора, над которой я работал, я использовал WM_CHAR для обработки ввода с клавиатуры. Однако я обнаружил, что некоторые из сообщений персонажей не записываются. Например, если я использую клавишу [shift] + number для ввода символа, такого как% или &, некоторые повторно записаны, тогда как другие, такие как [shift] +9 (что приводит к ')'), не записываются. Итак, мне интересно, должен ли я использовать пару WM_KEYDOWN/WMKEYUP для обработки ввода с клавиатуры. Я как-то написал кейлоггер в сборке (на самом деле это был всего лишь учебник, который я тестировал) и использовал пары WM_KEYDOWN/WM_KEYUP, и это получилось неплохо. Итак, следует ли мне переходить к этому, или это что-то необычное, что происходит с моей программой?

Спасибо,

Devjeet

4b9b3361

Ответ 1

Это действительно длинный ответ на ваш комментарий выше, но вставляя его в ответ, потому что он слишком длинный для комментария:)

Основная проблема для понимания здесь заключается в том, что ключи и символы не совсем то же самое. Некоторые (но не все) ключи генерируют символы; некоторые клавиши генерируют разные символы в зависимости от сдвига или другого состояния клавиатуры. И для реализации редактора вам необходимо обрабатывать как текстовый ввод, так и нетекстовый ввод клавиатуры, например, клавиши со стрелками. Теперь длинная версия, отбирающая из чего-то неправильное предположение:

 Видимо, окна работают по-настоящему странно. [...] Кажется, что когда вы нажимаете [shift] +9, окна отправляют VK_LEFT в wParam сообщения WM_CHAR

Похоже, вы можете смешивать две вещи здесь. Вещь с WM_CHAR заключается в том, что она дает вам коды символов для текстовых символов: поэтому, если кто-то нажимает клавишу 9, вы получите "9". Если кто-то нажал SHIFT + 9, Windows примет во внимание состояние переключения - и вы получите '(' (если используете клавиатуру США). Но вы никогда не получите WM_CHAR для клавиш со стрелками, HOME, END и т.д. поскольку они не являются текстовыми символами. С другой стороны, WM_KEYDOWN не обрабатывается символами, а в кодах VK_, поэтому нажатие 9 дает вам VK_9 независимо от состояния сдвига, а стрелка влево дает вам VK_LEFT - снова рассматривает состояние сдвига.

Дело в том, что WM_CHAR и WM_KEYDOWN дают вам две части общего входного изображения - но вам действительно нужно обрабатывать оба изображения, чтобы получить полную картину. И имейте в виду, что wParam - это совсем другое дело в обоих случаях. Это код символа для WM_CHAR, но код VK_ для WM_KEYDOWN. Не смешивайте их.

И чтобы сделать вещи более запутанными, значения VK_ имеют те же значения, что и допустимые символы. Откройте WinUser.h(он входит в каталог include в каталоге установки компилятора) и найдите VK_LEFT:

#define VK_LEFT           0x25

Оказывается, что 0x25 также является кодом для символа "%" (подробности см. в любой таблице ascii/unicode). Поэтому, если WM_CHAR получает 0x25, это означает, что shift-5 был нажат (при условии, что клавиатура США) создает "%"; но если WM_KEYDOWN получает 0x25, это означает, что нажата стрелка влево (VK_LEFT). И чтобы добавить немного больше путаницы, коды виртуального ключа для клавиш AZ и клавиш 0-9 оказались такими же, как символы "A" - "Z" и "0" - "9", что делает его похожим на символы и VK_ взаимозаменяемы. Но это не так: код для нижнего регистра "a", 0x61, - VK_NUMPAD1! (Таким образом, получение 0x61 в WM_CHAR означает "a", получение его в WM_KEYDOWN означает NUMPAD 1. И если пользователь нажимает клавишу "A" в состоянии без сдвига, то вы фактически получаете VK_A (то же значение, что и "A" ) в WM_KEYDOWN, который переводится в WM_CHAR из 'a'.)

Таким образом, связывая все это вместе, типичным способом обработки клавиатуры является использование всего следующего:

  • Используйте WM_CHAR для обработки текстового ввода: фактические текстовые клавиши. wParam - это символ, который вы хотите добавить в свою строку, или сделать что-нибудь еще. Это делает всю обработку сдвига для вас.

  • Используйте WM_KEYDOWN для обработки "мета" ключей - например, клавиш со стрелками, дома, конца, страницы и т.д. Передайте все значения A-Z/0-9, обработка по умолчанию превратит их в WM_CHAR, которые вы можете обрабатывать в вашем обработчике WM_CHAR. (Здесь вы также можете обрабатывать клавиши numpad, если вы хотите использовать их для специальных функций, иначе они "проваливаются", чтобы заканчиваться как числовые WM_CHAR, в зависимости от состояния numlock. Windows заботится об этом, точно так же, как обрабатывает состояние сдвига для алфавитные клавиши.)

  • Если вы хотите явно обрабатывать ALT-комбо (вместо использования таблицы ускорителей), вы получите их через WM_SYSKEYDOWN.

Я думаю, что есть некоторые ключи, которые могут отображаться в обоих - Enter может отображаться как WM_KEYDOWN из VK_RETURN, так и как \r или\n WM_CHAR, но я предпочитаю обрабатывать его в WM_KEYDOWN, чтобы редактировать управление ключами отдельно от текстовых клавиш.

Ответ 2

Spy ++ покажет вам сообщения, отправляемые в окно, чтобы вы могли экспериментировать и видеть, какие сообщения подходят для вашего приложения.

Если у вас установлена ​​Visual Studio, она должна находиться в меню "Пуск" в меню "Программы" → "Microsoft Visual Studio" → "Инструменты Visual Studio" → "Шпион ++".

Ответ 3

Приведенное выше полезное сообщение вдохновило меня на создание этого фрагмента, который дает удобочитаемое представление о том, какая клавиша была нажата с любого WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP независимо от состояния клавиш-модификаторов.

// get the keyboard state
BYTE keyState[256];
GetKeyboardState(keyState);
// clear all of the modifier keys so ToUnicode will ignore them
keyState[VK_CONTROL] = keyState[VK_SHIFT] = keyState[VK_MENU] = 0;
keyState[VK_LCONTROL] = keyState[VK_LSHIFT] = keyState[VK_LMENU] = 0;
keyState[VK_RCONTROL] = keyState[VK_RSHIFT] = keyState[VK_RMENU] = 0;
// convert the WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP to characters
UINT scanCode = (inLParam >> 16) & 0xFF;
int i = ToUnicode(inWParam, scanCode, keyState, outBuf, inOutBufLenCharacters, 0);
outBuf[i] = 0;

Изменяя массив keyState так, чтобы все клавиши-модификаторы были чистыми, ToUnicode всегда будет выводить нажатую несмещенную клавишу. (Таким образом, на английской клавиатуре вы никогда не получите "%", но всегда "5"), если это удобочитаемая клавиша. Тем не менее, вы все равно должны выполнить проверку VK_XXX, чтобы почувствовать стрелку и другие нечитаемые человеком клавиши.

(Я пытался настроить редактируемую пользователем систему "горячих клавиш" в моем приложении, и различие между WM_KEYXXX и WM_CHAR сводило меня с ума. Приведенный выше код решил эту проблему.)