ToUnicodeEx правильная обработка ключей состояния (Shift, Alt-gr) в низкоуровневом хуке - PullRequest
0 голосов
/ 10 мая 2019

У меня есть хук низкого уровня, но я обнаружил, что toUnicodeEx не может правильно обрабатывать состояния Shift- и Alt-gr. Я нашел исправление для Shift, но не для Alt-gr.

У меня есть задача по автоматизации, и поэтому я столкнулся с этой проблемой, используя язык программирования AutoIt (базовый тип), но он кажется универсальным, я обнаружил множество аналогичных описаний проблем от людей, использующих другие языки. К сожалению, ни одно из представленных решений не сработало.

Я понял, что в низкоуровневом хуке, который отслеживает конкретное приложение (некоторые люди называют это кейлоггером), перевод кода сканирования в реальный код символа с помощью ToUnicodeEx является проблемой, поскольку информация о состоянии клавиатуры и раскладке клавиатуры из процесса мониторинга вместо отслеживаемого процесса.

Обычно все приложения используют одну и ту же раскладку клавиатуры по умолчанию, поэтому я могу использовать GetKeyboardLayout (GetDesktopWindow ()) и получать результаты, которые для меня достаточно хороши. К сожалению, это обеспечивает только правильное отображение строчных букв a-z.

Чтобы получить смещенные буквы, я понял, что мне нужен результат функции getKeyboardState, но это работает только для нажатий клавиш, отправляемых процессу, владеющему обработчиком низкоуровневых перехватов клавиатуры. Но поскольку структура представляет собой простой массив, содержащий байт состояния для каждого виртуального ключа, я попытался заменить его собственным и установить байты состояния в соответствии с моими потребностями.

Это хорошо работает для клавиш [Shift], но не для [Alt-gr].

?? Как мне заставить Alt-gr перевод работать в ToUnicodeEx ??

Func LowLevelKeyboardProc($nCode, $wParam, $lParam)

If $nCode < 0 Then
   Return _WinAPI_CallNextHookEx($hK_Hook, $nCode, $wParam, $lParam)
EndIf

Local $tKEYStruct = DllStructCreate($tagKBDLLHOOKSTRUCT, $lParam)
local $vkCode = DllStructGetData($tKEYStruct, "vkCode")
local $scanCode = DllStructGetData($tKEYStruct, "scanCode")

If $wParam = $WM_KEYUP Then ;Key Up

      ; create a zeroed 256 byte array
  Local $tKeyboardState = DllStructCreate("byte[256]")
      ; set the keystate flag(s)
  if $LShiftState = true Then
     DLLStructSetData($tKeyboardState, 1, 0x81, $VK_CAPITAL + 1)    ; Shift
  EndIf
  If $AltGrState Then
     DLLStructSetData($tKeyboardState, 1, 0x80, $VK_MENU + 1)   ; Alt-Gr
     DLLStructSetData($tKeyboardState, 1, 0x80, $VK_CONTROL + 1)
  EndIf

  Local $keyBuffer = ""
  Local $result = _User32_ToUnicodeEx($vkCode, $scanCode, DllStructGetPtr($tKeyboardState) , $keyBuffer, 5,0, _WinAPI_GetKeyboardLayout(_WinAPI_GetDesktopWindow ( )))
  ConsoleWrite("Key Up " & $result & " Scancode: 0x" & Hex($scanCode) & " VirtualkeyCode: 0x" & Hex($vkCode) & " [" & $keyBuffer & "]" & @CRLF)
  if $result > 0 then

  endif
   Switch $vkCode
   case $VK_LSHIFT
      $LShiftState = false
   case $VK_RMENU
      $AltGrState = false
   EndSwitch
Else    ; Key Down
  ; ConsoleWrite("Key Dwn " & " Scancode: 0x" & Hex($scanCode) & " VirtualkeyCode: 0x" & Hex($vkCode) & @CRLF)
   Switch $vkCode
   case $VK_LSHIFT
      $LShiftState = true
   case $VK_RMENU
      $AltGrState = true
   EndSwitch
EndIf
$tKeyboardState = 0 ; free memory
Return _WinAPI_CallNextHookEx($hK_Hook, $nCode, $wParam, $lParam)
EndFunc

Этот подход почти работает. Мне удобно использовать раскладку клавиатуры по умолчанию, приложения обычно не используют разную раскладку клавиатуры. Состояния [Shift] корректно обрабатываются, например, ToUnicodeEx возвращает "!" если я впихну в скан- и вк-код клавиши «1». Кроме того, все специальные клавиши символов, например, Немецкие умлауты (äöüÄÖÜß) работают.

К сожалению, если я установлю комбинации клавиш Alt и Ctrl (также безуспешно пробовал VK_RMENU), я не получу такие символы Alt-gr, как (немецкая клавиатура) @, €, {, [,],}, \ and и так далее.

Обратите внимание, что подпрограмма работает, если вызывается напрямую (без использования подключаемой процедуры), поэтому я чувствую, что почти на месте.

Пожалуйста, не чувствуйте себя отчужденным, потому что я предоставляю код AutoIt, он похож на другие языки, я думаю, что проблема связана с API, а не языком. Autoit предоставляет некоторые средства для взаимодействия с Windows API, а именно три важные функции:

DllStructCreate - создать байтовую структуру, которую можно передавать в API-интерфейсы Windows ...

DllStructGetPtr - получить указатель на структуру, которую можно передать в API Windows

DLLStructGetData, DLLStructSetData - доступ к структуре для чтения или изменения байтов (примечание: нумерация байтов основана на 1)

AutoIt также предоставляет обширную библиотеку функций, которые обертывают функции Windows API, схема именования всегда WinAPI + имя функции Windows, поэтому, например, _WinAPI_CallNextHookEx () вызывает CallNextHookEx () и _User32_ToUnicodeEx соответствует ToUnicodeEx ().

...