2013-07-27 87 views
1

SetWindowsHookExWH_KEYBOARD_LL之间的消息转换为所按键的有用表示形式的正确方法是什么?解释SetWindowsHookEx/WH_KEYBOARD_LL的正确逻辑

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

从广义上说,似乎有三种情形:

  • 特殊键按下(按Ctrl /退出/移位/ ALT)
  • 标准键按下(A-Z0-9等..请注意,aA都解读为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的Source Engine可以做到这一点。我同意这会让生活更轻松。 – Basic

回答

2

根据语言(及其对死键的使用),这可能非常复杂。 ToAsciiEx适用于简单的情况,但如果涉及到死锁或IME,事情会非常迅速地变得非常复杂。

Michael Kaplan's blog拥有广泛的系列文章中谈到的键盘布局和Keyboard Layout Creator,这是一个用于创建键盘布局(映射按键字符的最简单方法)的工具。当一组键盘输入映射到单个字符时,将使用键盘布局创建器。在这些情况下,可以完成映射,但当需要多个关键字来确定一个字符时,执行映射就有点棘手。

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

+0

谢谢埃里克,这是非常有用的,同时也满足了我的好奇心。 – Basic

1

埃里克的绝对正确,没有一个简单的方法来做到这一点。我附上了我的尽力而为的代码,其中我得到了95%的使用方式(使用ToUnicodeEx)。

它没有赶上特殊(IME /死键)正确的字符(的AltGr - 对英国英语键盘)。它的确如此,但是,preserve the state of the kernel-mode keyboard buffer(所以它不会影响键入的高级组合键)。

请注意,此代码不是生产就绪,并需要适当的错误处理等等,它也只在几个键盘布局上进行测试。因人而异。

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