2013-07-27 4 views
1

Каков правильный способ преобразования сообщения от SetWindowsHookEx с WH_KEYBOARD_LL в полезное представление нажатой клавиши?Правильная логика для интерпретации SetWindowsHookEx/WH_KEYBOARD_LL

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

Грубо говоря там, кажется, три сценария:

  • Специальная клавиша нажата (Ctrl/Бегство/Shift/Alt)
  • Стандартный ключ нажат (A-Z, 0-9 и т.д ... Обратите внимание, что a и A как читать A)
  • Некоторые трудно определить случаи, как цифровая клавиатура и F1 - F12

Специальные клавиши могут быть рассмотрены по мере необходимости и есть некоторые полезные поиски в System.Windows.Forms.Keys

, но если бы я должен был сделать (по Великобритании английской клавиатуры) восклицательный знак, она определяется как Shift-внизсдвиг вверх

Поскольку я перекинув на слишком низком уровне (я считаю), чтобы получить коды после того, как они прошли через слой клавиатуры «преобразование», я антикварная вещь нас, как бы я правильно их интерпретировал.

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

+0

Проверьте функцию «ToAsciiEx». –

+0

Будет ли [RegisterHotKey] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms646309.aspx) предоставить функциональность, которую вы ищете, или это не очень хорошо справится с играми/DirectInput? – IInspectable

+0

Спасибо за предложение @Tim, это на самом деле я начал. К сожалению, с этой страницы «RegisterHotKey терпит неудачу, если нажатые клавиши для горячей клавиши уже зарегистрированы другой горячей клавишей». И некоторые игры, похоже, используют эту технику (или, по крайней мере, используют аналогичный метод, который также блокирует его). Двигатель источника IIRC Valve делает это. Я согласен, что это облегчит жизнь. – Basic

ответ

2

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

Michael Kaplan's blog имеет обширную серию статей, посвященных раскладкам клавиатуры и Keyboard Layout Creator, который является инструментом для создания раскладок клавиатуры (самый простой способ сопоставления клавиш с символами). Создатель макета клавиатуры используется, когда один набор нажатий клавиш отображается на один символ. В этих случаях можно сделать сопоставление, но сделать несколько сложнее, если для определения персонажа требуется более одного ключа.

Языки, такие как японский, китайский или корейский, не имеют единого отображения от нажатий клавиш до символов. Для этих языков необходим редактор метода ввода; они обычно используют Text Services Framework, который представляет собой обширный набор API-интерфейсов, которые используют редакторы методов ввода (и другие текстовые службы, такие как распознавание рукописного ввода и распознавание речи) для связи с приложением, чтобы определить отображение от нажатий клавиш до символов. Поскольку отображение нажатий клавиш на символы с IME определяется кодом, низкоуровневый манипулятор клавиатуры часто не может выполнять это сопоставление вообще.

+0

Спасибо Эрик, это было невероятно полезно и удовлетворено моим любопытством в то же время. – Basic

1

Эрик абсолютно прав, что нет простого способа сделать это.Я прикрепляю свой код с наилучшим усилием, ниже которого получается 95% пути (используя ToUnicodeEx).

Это не улавливает специальный (IME/Dead ключ) символы правильно (AltGr - для на UK-английской клавиатуры). Это, однако, preserve the state of the kernel-mode keyboard buffer (так что это не мешает использованию расширенных клавишных клавиш).

Обратите внимание, что этот код не готов для производства и требует правильной обработки ошибок и т. Д. И т. Д. Он также проверяется только на нескольких раскладках клавиатуры. YMMV.

Public Declare Function UnhookWindowsHookEx Lib "user32" (
    ByVal hHook As Integer) As Integer 

Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (
    ByVal idHook As Integer, 
    ByVal lpfn As KeyboardHookDelegate, 
    ByVal hmod As Integer, 
    ByVal dwThreadId As Integer) As Integer 

Private Declare Function GetAsyncKeyState Lib "user32" (
    ByVal vKey As Integer) As Integer 

Private Declare Function CallNextHookEx Lib "user32" (
    ByVal hHook As Integer, 
    ByVal nCode As Integer, 
    ByVal wParam As Integer, 
    ByVal lParam As KBDLLHOOKSTRUCT) As Integer 

Public Structure KBDLLHOOKSTRUCT 
    Public vkCode As Integer 
    Public scanCode As Integer 
    Public flags As Integer 
    Public time As Integer 
    Public dwExtraInfo As Integer 
End Structure 

' Low-Level Keyboard Constants 
Private Const HC_ACTION As Integer = 0 
Private Const LLKHF_EXTENDED As Integer = &H1 
Private Const LLKHF_INJECTED As Integer = &H10 
Private Const LLKHF_ALTDOWN As Integer = &H20 
Private Const LLKHF_UP As Integer = &H80 

Private Const WH_KEYBOARD_LL As Integer = 13& 
Public KeyboardHandle As Integer 

Public Declare Function ToUnicodeEx Lib "user32" (wVirtKey As UInteger, wScanCode As UInteger, lpKeyState As Byte(), <Out()> ByVal lpChar As System.Text.StringBuilder, cchBuff As Integer, wFlags As UInteger, dwhkl As IntPtr) As Integer 
Public Declare Function GetKeyboardState Lib "user32" (lpKeyState As Byte()) As Boolean 
Public Declare Function GetKeyState Lib "user32" (keyCode As Integer) As Short 


Private Function ConvertToUnicode(lParam As KBDLLHOOKSTRUCT) As String 
    Select Case lParam.vkCode 
     Case 8 
      Return "{Backspace}" 
     Case 9 
      Return "{Tab}" 
     Case Else 

      Dim SB As New System.Text.StringBuilder() 
      Dim KeyState(255) As Byte 

      'The output from this isn't actually used but it forces the Api 
      'to evaluate the modifiers for the key code 
      Dim ModifierState As Integer 
      GetKeyState(ModifierState) 

      Dim KeyStateStatus As Boolean = GetKeyboardState(KeyState) 

      If Not KeyStateStatus Then 
       Return "" 
      End If 

      ToUnicodeEx(CUInt(lParam.vkCode), 
         CUInt(lParam.scanCode), 
         KeyState, SB, SB.Capacity, 0, 
         InputLanguage.CurrentInputLanguage.Handle) 
      Return SB.ToString() 
    End Select 
End Function 

Public Function KeyboardCallback(ByVal Code As Integer, 
           ByVal wParam As Integer, 
           ByRef lParam As KBDLLHOOKSTRUCT) As Integer 
    Try 
     Dim Key As String = Nothing 
     If (lParam.flags And LLKHF_EXTENDED) = 0 Then 
      If (lParam.flags And LLKHF_UP) = 0 Then 
       Key = ConvertToUnicode(lParam) 
      End If 
     Else 
      Dim KeyCode = DirectCast(lParam.vkCode, System.Windows.Forms.Keys) 
      If KeyCode <> System.Windows.Forms.Keys.RShiftKey And 
       KeyCode <> System.Windows.Forms.Keys.LShiftKey Then 
       If (lParam.flags And LLKHF_UP) = 0 Then 
        'Special Key pressed 
        Key = "{" & KeyCode.ToString & "+}" 
       Else 
        'Special Key released 
        Key = "{" & KeyCode.ToString & "-}" 
       End If 
      End If 
     End If 

     If Key IsNot Nothing Then 
      Debug.WriteLine(Key) 
     End If 

    Catch ex As Exception 
     'Do something!  
    End Try 

    Return CallNextHookEx(KeyboardHandle, Code, wParam, lParam) 
End Function