Blog. Just Blog

Эмуляция нажатия мультимедийных клавиш на клавиатуре

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
В статье про обработку мультимедийных кнопок я немного коснулся эмуляции их нажатий. Там эмуляция была направлена напрямую в оболочку, сейчас же я расскажу, как выполнять такое действие полноценно, с учетом интересов всех запущенных приложений. Начнем с эмуляции пользовательского ввода с помощью функции SendInput.

При всей моей любви к FASM, просто убивает скудность его инклудов. Похоже, что наполнение их данными остановилось на Win2000, а если захочется что-нибудь чуть новее, то все, добро пожаловать в MSDN. И хорошо, если структура окажется на пять-десять записей. Вот и сейчас нам придется самостоятельно описать структуру INPUT и обозначить коды мультимедийных клавиш.
  1. struct MOUSEINPUT
  2.         dx          dd ?
  3.         dy          dd ?
  4.         mouseData   dd ?
  5.         dwFlags     dd ?
  6.         time        dd ?
  7.         dwExtraInfo dd ?
  8. ends
  9.  
  10. struct KEYBDINPUT
  11.         wVk         dw ?
  12.         wScan       dw ?
  13.         dwFlags     dd ?
  14.         time        dd ?
  15.         dwExtraInfo dd ?
  16. ends
  17.  
  18. struct HARDWAREINPUT
  19.         uMsg        dd ?
  20.         wParamL     dw ?
  21.         wParamH     dw ?
  22. ends
  23.  
  24. struct INPUT
  25.         type        dd ?
  26.         union
  27.             mi      MOUSEINPUT
  28.             ki      KEYBDINPUT
  29.             hi      HARDWAREINPUT
  30.         ends
  31. ends
  32.  
  33. INPUT_KEYBOARD = 1
  34.  
  35. VK_VOLUME_UP   = 0xAF
  36. VK_VOLUME_DOWN = 0xAE
  37. VK_LAUNCH_APP2 = 0xB7
Структура INPUT задана через union, так как в зависимости от типа использования, внутри нее будет структура с разным количеством и разрядностью полей. В нашем случае будет эмуляция только клавиатурного ввода, но на будущее могут пригодиться и другие структуры.

Вот пара примеров, как можно эмулировать нажатия на мультимедийные клавиши. Увеличить громкость:
  1.         ; Нажать клавишу
  2.         mov     [input.type],INPUT_KEYBOARD
  3.         mov     [input.ki.wVk],VK_VOLUME_UP
  4.         mov     [input.ki.wScan],0x30
  5.         mov     [input.ki.dwFlags],KEYEVENTF_EXTENDEDKEY
  6.         invoke  SendInput,1,input,sizeof.INPUT
  7.         ; Отпустить клавишу
  8.         mov     [input.ki.dwFlags],KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP
  9.         invoke  SendInput,1,input,sizeof.INPUT
Уменьшить громкость:
  1.         ; Нажать клавишу
  2.         mov     [input.type],INPUT_KEYBOARD
  3.         mov     [input.ki.wVk],VK_VOLUME_DOWN
  4.         mov     [input.ki.wScan],0x2E
  5.         mov     [input.ki.dwFlags],KEYEVENTF_EXTENDEDKEY
  6.         invoke  SendInput,1,input,sizeof.INPUT
  7.         ; Отпустить клавишу
  8.         mov     [input.ki.dwFlags],KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP
  9.         invoke  SendInput,1,input,sizeof.INPUT
Впрочем, эмулировать можно не только мультимедийные кнопки, а вообще любую кнопку, которая есть или могла бы быть на вашей клавиатуре. Например, запуск калькулятора (по умолчанию) или другого забинденного на эту кнопку приложения:
  1.         ; Нажать клавишу
  2.         mov     [input.type],INPUT_KEYBOARD
  3.         mov     [input.ki.wVk],VK_LAUNCH_APP2
  4.         mov     [input.ki.wScan],0x21
  5.         mov     [input.ki.dwFlags],KEYEVENTF_EXTENDEDKEY
  6.         invoke  SendInput,1,input,sizeof.INPUT
  7.         ; Отпустить клавишу
  8.         mov     [input.ki.dwFlags],KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP
  9.         invoke  SendInput,1,input,sizeof.INPUT
Коды и обозначения Virtual-Key, которые не знает FASM, смотрим здесь, а скан-коды клавиш здесь (колонка "Код нажатия XT").

В процессе тестирования я сделал интересные наблюдения. При изменении громкости аудиоплеер AIMP и мультимедийный плеер Light Alloy отслеживали подобные нажатия и меняли свой собственный уровень громкости, тогда как общий уровень громкости системы оставался неизменным. Когда ни один плеер не работал, то менялась общая громкость звука в системе, такое же поведение показывал и Windows Media Payer, который игнорировал "нажатия" на мультимедийные кнопки.

Второй способ более интересный. Он немного похож на пример из базовой статьи и также основан на отправке сообщения WM_APPCOMMAND. Сперва, как обычно, несколько констант для описания сообщения и кодов мультимедийных кнопок:
  1. WM_APPCOMMAND = 0x319
  2.  
  3. APPCOMMAND_VOLUME_DOWN = 9
  4. APPCOMMAND_VOLUME_UP   = 10
Чтобы сэмулировать нажатие мультимедийной кнопки при наступлении определенного события, мы отправляем сообщение WM_APPCOMMAND с параметром этой кнопки собственному окну:
  1.         ; Сообщение о нажатой мультимедийной клавише
  2.         invoke  SendMessage,[hwnddlg],WM_APPCOMMAND,[hwnddlg],\
  3.                 APPCOMMAND_VOLUME_UP shl 16
Обработчик главного окна приложения в этом случае должен включать в себя подобную конструкцию:
  1.         cmp     [msg],WM_APPCOMMAND
  2.         je      .wm_media
  3.         ...
  4.         ...
  5.         ...
  6. .wm_media:
  7.         ; Отправить сообщение дальше по цепочке обработчиков
  8.         invoke  DefWindowProc,[hwnddlg],[msg],[wparam],[lparam]
  9.         jmp     .processed
Казалось бы, что за дикая дичь, отправлять самому себе какое-то сообщение, а потом еще и не обрабатывать его. А логика тут следующая. Если мы не знаем конкретного получателя нашего сообщения, то должны отправить это сообщение по цепочке обработчиков. Его получат все приложения, которые установили хуки на сообщения, а в конечном итоге это сообщение получит оболочка. Ну или не получит, если какое-то приложение решит не передавать его дальше. Именно поэтому наше приложение и использует функцию DefWindowProc для инициирования подобной операции. Почему просто не использовать широковещательную отправку сообщения всем окнам через HWND_BROADCAST? Потому что в случае с мультимедийными кнопками это очень скверная практика, которая может привести к непредсказуемым последствиям. Окна приложений в обычном состоянии получают WM_APPCOMMAND только в активном состоянии, тогда как через установку хука они смогут получать сообщение в любой момент, когда им нужно. Нарушать это поведение широковещательной нецелевой рассылкой не надо. Если знаете конкретного получателя и уверены в его поведении, то не вопрос, отправляйте сообщение WM_APPCOMMAND напрямую этому окну.

В подтверждение этой мысли, Windows Media Payer прекрасно реагирует на эмуляцию кнопки Play/Pause, даже находясь в фоновом режиме, потому что как раз использует технику хуков. Другие необработанные сообщения, например, запуск калькулятора, благополучно достигают оболочки, которая, в свою очередь, обрабатывает их по своему усмотрению.

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

Send.Multimedia.Keys.Demo.zip (4,441 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (22.06.2020 в 23:26):
Шайтанама :)
АндрейК (22.06.2020 в 12:09):
Эх... Было дело: https://www.youtube.com/watch?v=gsSPKbf1Mjw

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

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

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