Обрабатывать ввод с клавиатуры в win32, WM_CHAR или WM_KEYDOWN / WM_KEYUP? - PullRequest
18 голосов
/ 17 ноября 2011

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

Спасибо

Devjeet

Ответы [ 3 ]

59 голосов
/ 18 ноября 2011

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

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

По-видимому, Windows работает очень странным образом. [...] Кажется, что когда вы нажимаете [shift] +9, Windows отправляет 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 означает NUMPAD1. И если пользователь нажимает клавишу «A» в несмещенном состоянии, то, что вы на самом деле получаете, это сначала VK_A (то же значение, что и «A») в WM_KEYDOWN, который переводится в WM_CHAR 'a'.)

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

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

  • Используйте WM_KEYDOWN для обработки мета-клавиш - таких как клавиши со стрелками, home, end, page up и так далее. Пропустите все значения A-Z / 0-9, обработка по умолчанию превратит их в WM_CHAR, которые вы можете обработать в своем обработчике WM_CHAR. (Вы также можете обрабатывать цифровые клавиши здесь, если хотите использовать их для специальных функций; в противном случае они «проваливаются», превращаясь в числовые WM_CHAR, в зависимости от состояния numlock. Windows заботится об этом так же, как обрабатывает состояние сдвига для буквенные клавиши.)

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

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

4 голосов
/ 17 ноября 2011

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

Если у вас установлена ​​Visual Studio, она должнанаходиться в меню «Пуск» в разделе «Программы» -> «Microsoft Visual Studio» -> «Инструменты Visual Studio» -> «Spy ++».

0 голосов
/ 01 апреля 2019

Полезное сообщение, приведенное выше, вдохновило меня на создание этого фрагмента, который дает вам удобочитаемое указание того, какая клавиша была нажата с любого 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 сводило меня с ума. Приведенный выше код решил эту проблему.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...