将SetWindowsHookEx
与WH_KEYBOARD_LL
的邮件转换为按下的密钥的有用表示的正确方法是什么?
我知道这很可能涉及了解本地键盘的映射。 (注意:我只考虑按键的时间,而不是为了简单起见而发布的)
从广义上讲,似乎有三种情况:
A-Z
,0-9
等...请注意a
和A
均为A
} 可以根据需要处理特殊键,System.Windows.Forms.Keys
但是如果我(在英国 - 英语键盘上)做一个惊叹号,它会被检测为 Shift-Down 1 Shift向上
因为我已经通过键盘"转换&#34层,我很好奇我是如何正确解释它们的。
至于我为什么要这样做。它开始是为我提供方便快捷方式的一种方式,我写的可以在任何地方工作,甚至在游戏内部(一些游戏似乎拦截击键并阻止它们传播到操作系统)。我实际上得到了足够的东西,只要它是我使用应用程序(只是可能的用户),但我的好奇心是如何我可以进一步,如果我想这样做。
答案 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