用于解释SetWindowsHookEx / WH_KEYBOARD_LL的正确逻辑

时间:2013-07-27 19:46:40

标签: .net winapi user32 setwindowshookex wh-keyboard-ll

SetWindowsHookExWH_KEYBOARD_LL的邮件转换为按下的密钥的有用表示的正确方法是什么?

我知道这很可能涉及了解本地键盘的映射。 (注意:我只考虑按键的时间,而不是为了简单起见而发布的)

从广义上讲,似乎有三种情况:

  • 按下特殊键(Ctrl / Escape / Shift / Alt)
  • 按下标准键(A-Z0-9等...请注意aA均为A}
  • 有些难以定义的情况,如数字键盘和 F1 - F12

可以根据需要处理特殊键,System.Windows.Forms.Keys

中有一些有用的查找

但是如果我(在英国 - 英语键盘上)做一个惊叹号,它会被检测为 Shift-Down 1 Shift向上

因为我已经通过键盘"转换&#34层,我很好奇我是如何正确解释它们的。

至于我为什么要这样做。它开始是为我提供方便快捷方式的一种方式,我写的可以在任何地方工作,甚至在游戏内部(一些游戏似乎拦截击键并阻止它们传播到操作系统)。我实际上得到了足够的东西,只要它是我使用应用程序(只是可能的用户),但我的好奇心是如何我可以进一步,如果我想这样做。

2 个答案:

答案 0 :(得分:2)

根据语言(及其使用死键),这可能非常复杂。 ToAsciiEx适用于简单的情况,但如果涉及死钥匙或IME,事情会很快变得非常复杂。

Michael Kaplan's blog有一系列文章讨论键盘布局和Keyboard Layout Creator,这是一种创建键盘布局的工具(将键映射到字符的最简单方法)。当一组击键映射到单个字符时,使用键盘布局创建器。在这些情况下,可以进行映射,但是当需要多个键来确定字符时,它会变得有些棘手。

日语,中文或韩语等语言没有从击键到字符的单一映射。对于这些语言,需要输入法编辑器;这些通常使用Text Services Framework,这是一组广泛的API,用于输入方法编辑器(以及其他文本服务,如手写识别和语音识别),用于与应用程序通信以确定从击键到字符的映射。由于从击键到使用IME的字符的映射是由代码定义的,因此低级键盘钩子通常根本无法执行此映射。

答案 1 :(得分:1)

Eric绝对正确,没有一种简单的方法可以做到这一点。我在下面添加了尽力而为的代码,它使我获得了95%的使用权限(使用ToUnicodeEx)。

它没有正确捕获特殊(IME /死键)字符(英国英语键盘上的 AltGr - 4 )。但它确实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