Blog. Just Blog

Разделение ввода от нескольких клавиатур

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Разделение ввода от нескольких клавиатур
Разделение ввода от нескольких клавиатур

В одной из прошлых статей мы научились получать список всех устройств, реальных и виртуальных, которые используются для ввода. А можно ли каким-то образом разделять данные, полученные от разных источников? Например, к компьютеру подключены несколько клавиатур и надо определить, на какой именно клавиатуре была нажата клавиша. Ответ - да, можно. Приложение может подписаться на нужные устройства ввода при помощи функции RegisterRawInputDevices. При получении "сырого" ввода от каждого из устройств выбранного типа, система будет посылать приложению сообщение WM_INPUT, а приложению останется его правильно обработать.

Теперь константы и структуры, про которые не знает FASM, но которые нам понадобятся для работы. Даже немного с избытком, но это на будущее, вдруг когда-нибудь пригодится.
  1. struct RAWINPUTDEVICE
  2.     usUsagePage dw ?
  3.     usUsage     dw ?
  4.     dwFlags     dd ?
  5.     hwndTarget  dd ?
  6. ends
  7.  
  8. struct RAWMOUSE
  9.     usFlags dw ?
  10.     union
  11.         ulButtons dd ?
  12.         union
  13.             usButtonFlags dw ?
  14.             usButtonData  dw ?
  15.         ends
  16.     ends
  17.     ulRawButtons dd ?
  18.     lLastX dd ?
  19.     lLastY dd ?
  20.     ulExtraInformation dd ?
  21. ends
  22.  
  23. struct RAWKEYBOARD
  24.     MakeCode dw ?
  25.     Flags    dw ?
  26.     Reserved dw ?
  27.     VKey     dw ?
  28.     Message  dd ?
  29.     ExtraInformation dd ?
  30. ends
  31.  
  32. struct RAWHID
  33.     dwSizeHid dd ?
  34.     dwCount   dd ?
  35.     bRawData  db ?
  36. ends
  37.  
  38. struct RAWINPUTHEADER
  39.     dwType  dd ?
  40.     dwSize  dd ?
  41.     hDevice dd ?
  42.     wParam  dd ?
  43. ends
  44.  
  45. struct RAWINPUT
  46.     header RAWINPUTHEADER
  47.     union
  48.         mouse    RAWMOUSE
  49.         keyboard RAWKEYBOARD
  50.         hid      RAWHID
  51.     ends
  52. ends
  53.  
  54. HID_USAGE_GENERIC_KEYBOARD = 6
  55. HID_USAGE_PAGE_GENERIC = 1
  56.  
  57. RIDEV_REMOVE = 1
  58.  
  59. RIM_TYPEMOUSE    = 0
  60. RIM_TYPEKEYBOARD = 1
  61. RIM_TYPEHID      = 2
  62.  
  63. RI_KEY_MAKE  = 0
  64. RI_KEY_BREAK = 1
  65. RI_KEY_E0    = 2
  66. RI_KEY_E1    = 4
  67.  
  68. RID_INPUT = 0x10000003
  69.  
  70. WM_INPUT  = 0x00FF
В момент инициализации окна приложения заполняем структуру RAWINPUTDEVICE. Usage Page и Usage ID заполняются по этому справочнику, в нашем случае это клавиатура. Но точно так же можно отслеживать мышь и другие устройства ввода. И обязательно указываем хэндл окна, которому будут отправляться сообщения WM_INPUT.
  1.         ; Зарегистрировать обработку ввода
  2.         mov     [dev.usUsagePage],HID_USAGE_PAGE_GENERIC
  3.         mov     [dev.usUsage],HID_USAGE_GENERIC_KEYBOARD
  4.         mov     [dev.dwFlags],0
  5.         mov     eax,[hwnddlg]
  6.         mov     [dev.hwndTarget],eax
  7.         invoke  RegisterRawInputDevices,dev,1,sizeof.RAWINPUTDEVICE
При закрытии приложения или когда необходимость в обработке ввода отпадет, подписку надо будет отменить:
  1.         ; Прекратить обработку ввода
  2.         mov     [dev.usUsagePage],HID_USAGE_PAGE_GENERIC
  3.         mov     [dev.usUsage],HID_USAGE_GENERIC_KEYBOARD
  4.         mov     [dev.dwFlags],RIDEV_REMOVE
  5.         mov     [dev.hwndTarget],0
  6.         invoke  RegisterRawInputDevices,dev,1,sizeof.RAWINPUTDEVICE
Когда приложению поступает сообщение WM_INPUT, получаем все параметры "сырого" ввода при помощи функции GetRawInputData. Делается это в два захода. Сперва получаем размер необходимой памяти для данных, а затем уже сами данные. После выполнения у нас есть заполненная структура RAWINPUT и подробности о клавиатурном событии в структуре RAWKEYBOARD.
  1.         ; Получено сообщение WM_INPUT?
  2.         cmp     [msg],WM_INPUT
  3.         je      .wminput
  4.         ...
  5.         ...
  6. .wminput:
  7.         ; Получить размер необходимых данных
  8.         invoke  GetRawInputData,[lparam],RID_INPUT,0,dSize,sizeof.RAWINPUTHEADER
  9.         ; Получить данные
  10.         invoke  GetRawInputData,[lparam],RID_INPUT,input,dSize,\
  11.                 sizeof.RAWINPUTHEADER
  12.         ; Ввод от клавиатуры?
  13.         cmp     [input.header.dwType],RIM_TYPEKEYBOARD
  14.         jne     .processed
В заголовке структуры RAWINPUT надо первым делом проверить поле dwType и убедиться, что событие ввода произошло действительно от клавиатуры. Оттуда же можно извлечь хэндл устройства, на котором была нажата или отпущена кнопка. Он соответствует хэндлу устройства, полученного из GetRawInputDeviceList, по нему же можно узнать подробную информацию о самом устройстве. Из структуры RAWKEYBOARD извлекаем тип сообщения (WM_KEYUP, WM_KEYDOWN и т.п.), виртуальный код клавиши, скан-код клавиши, наличие префикса для скан-кода а также действие с клавишей (нажата или отпущена). Если планируете использовать скан-код, то скорее всего придется воспользоваться таблицей преобразований скан-кодов из USB HID в PS/2. На мой взгляд, в большинстве случаев можно будет ограничиться виртуальными кодами.

Я протестировал обычные и мультимедийные клавиатуры с подключениями через USB и PS/2, беспроводную bluetooth-клавиатуру, клавиатуры ноутбуков. Способ везде уверенно работает, устройства определяются корректно, ввод можно разделять.

Что интересно, при тестировании водил кулаком по клавиатуре, в какой-то момент Punto Switcher углядел определенную комбинацию символов и переключил раскладку клавиатуры для последнего введенного "слова". В лог незамедлительно вывалилась целая пачка якобы нажатий и отпусканий кнопок. Получается, что так можно отслеживать программную эмуляцию клавиатурного ввода, в таких случаях значение поля hDevice всегда будет нулевым. Рискну предположить, что на чем-то подобном основана самозащита антивирусных программ от эмуляции действий пользователя.

В приложении пример программы с исходным текстом, которая обрабатывает ввод от нескольких клавиатур.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Keyboard.Raw.Input.Demo.zip (132,463 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 1571 | Комментариев: 8

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
FasmTheBest (23.04.2024 в 23:49):
GetRawInputBuffer позволяет за один вызов получить массив со всеми последними данными, полезно для устройств с очень частой отправкой. Спасибо Вам что обратили внимание на этот важный инструментарий.
ManHunter (20.05.2021 в 12:00):
Так если подключить клавиатуру или мышь к ноуту, то получится ровно такое же опасное деяние :)
Grey (20.05.2021 в 08:36):
Так ты опасный человек, на одну машину можешь несколько человек усадить)). Так гляди и занесут в черный список мирового правительства.
qaz (25.09.2020 в 18:52):
Fn кнопка существует только для контроллера клавы как переключатель
ManHunter (23.09.2020 в 17:17):
Ну при таких раскладах - да, действительно проблема.
0101 (23.09.2020 в 11:56):
мне до "ближайшего" магазина 100км (правда, почта это дело сильно упрощает) и клаву хочу за 2,5тр с usb-портами и котортким ходом клавиш, которую не сразу и найдёшь.. Может, прежняя потому и сгорела, что постоянно в неё флешку засовывал - удобно!
ManHunter (23.09.2020 в 08:58):
Так вроде ж не заря компьютерной эры, ипотеку на клавиатуру брать уже не надо :)
Все решение сводится к прогулке до ближайшего магазина электроники и нескольким сотням рублей.
0101 (23.09.2020 в 08:35):
Хорошая статья, кому-то обязательно пригодится! У меня более насущные потребности: сгорела одна клава, поставил какая была под рукой, а она "ноутбучная". Вместо привычного левого "Win" кнопка "Fn". Как переназначить (Win7_x64) так и не придумал, ни программами, ни правкой реестра не получилось.. Придётся новую клаву покупать.. Или на "Alt+D" повесить "Win+D"..

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.006 сек.) / Память: 4.5 Mb
Наверх