1

我有一个西班牙语键盘,所以它使用 ISO-LATIN,我的代码页是 1252。

我找到了一个键盘低级钩子类,并制作了一个代码来管理我需要的所有键盘键,例如在我的键盘中,“Keys.Oemtilde”将是“ñ”字符,所以我需要对大部分键进行硬编码。

如何在不进行硬修改的情况下为我打印正确的字符?:

MsgBox(ChrW(Keys.Oemtilde)) ' Result: À
' Correct result woould be: ñ

这是钩子类:

#Region " KeyboardHook Class "

Imports System.Runtime.InteropServices

Public Class KeyboardHook

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
    Private Overloads Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal HookProc As KBDLLHookProc, ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
    End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
    Private Overloads Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
    End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
    Private Overloads Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean
    End Function

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure KBDLLHOOKSTRUCT
        Public vkCode As UInt32
        Public scanCode As UInt32
        Public flags As KBDLLHOOKSTRUCTFlags
        Public time As UInt32
        Public dwExtraInfo As UIntPtr
    End Structure

    <Flags()> _
    Private Enum KBDLLHOOKSTRUCTFlags As UInt32
        LLKHF_EXTENDED = &H1
        LLKHF_INJECTED = &H10
        LLKHF_ALTDOWN = &H20
        LLKHF_UP = &H80
    End Enum

    Public Shared Event KeyDown(ByVal Key As Keys)
    Public Shared Event KeyUp(ByVal Key As Keys)

    Private Const WH_KEYBOARD_LL As Integer = 13
    Private Const HC_ACTION As Integer = 0
    Private Const WM_KEYDOWN = &H100
    Private Const WM_KEYUP = &H101
    Private Const WM_SYSKEYDOWN = &H104
    Private Const WM_SYSKEYUP = &H105

    Private Delegate Function KBDLLHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    Private KBDLLHookProcDelegate As KBDLLHookProc = New KBDLLHookProc(AddressOf KeyboardProc)
    Private HHookID As IntPtr = IntPtr.Zero

    Private Function KeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
        If (nCode = HC_ACTION) Then
            Dim struct As KBDLLHOOKSTRUCT
            Select Case wParam
                Case WM_KEYDOWN, WM_SYSKEYDOWN
                    RaiseEvent KeyDown(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))
                Case WM_KEYUP, WM_SYSKEYUP
                    RaiseEvent KeyUp(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))
            End Select
        End If
        Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)
    End Function

    Public Sub New()
        HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
        If HHookID = IntPtr.Zero Then
            Throw New Exception("Could not set keyboard hook")
        End If
    End Sub

    Protected Overrides Sub Finalize()
        If Not HHookID = IntPtr.Zero Then
            UnhookWindowsHookEx(HHookID)
        End If
        MyBase.Finalize()
    End Sub

End Class

#End Region

...这是我自己的代码:

#Region " KeyLogger "

Public WithEvents KeysHook As New KeyboardHook

Dim Auto_Backspace_Key As Boolean = True
Dim Auto_Enter_Key As Boolean = True
Dim Auto_Tab_Key As Boolean = True
Dim No_F_Keys As Boolean = False

Private Sub KeysHook_KeyDown(ByVal Key As Keys) Handles KeysHook.KeyDown

    Select Case Control.ModifierKeys

        Case 393216 ' Alt-GR + Key

            Select Case Key
                Case Keys.D1 : Key_Listener("|")
                Case Keys.D2 : Key_Listener("@")
                Case Keys.D3 : Key_Listener("#")
                Case Keys.D4 : Key_Listener("~")
                Case Keys.D5 : Key_Listener("€")
                Case Keys.D6 : Key_Listener("¬")
                Case Keys.E : Key_Listener("€")
                Case Keys.Oem1 : Key_Listener("[")
                Case Keys.Oem5 : Key_Listener("\")
                Case Keys.Oem7 : Key_Listener("{")
                Case Keys.Oemplus : Key_Listener("]")
                Case Keys.OemQuestion : Key_Listener("}")
                Case Else : Key_Listener("")
            End Select

        Case 65536 ' LShift/RShift + Key

            Select Case Key
                Case Keys.D0 : Key_Listener("=")
                Case Keys.D1 : Key_Listener("!")
                Case Keys.D2 : Key_Listener("""")
                Case Keys.D3 : Key_Listener("·")
                Case Keys.D4 : Key_Listener("$")
                Case Keys.D5 : Key_Listener("%")
                Case Keys.D6 : Key_Listener("&")
                Case Keys.D7 : Key_Listener("/")
                Case Keys.D8 : Key_Listener("(")
                Case Keys.D9 : Key_Listener(")")
                Case Keys.Oem1 : Key_Listener("^")
                Case Keys.Oem5 : Key_Listener("ª")
                Case Keys.Oem6 : Key_Listener("¿")
                Case Keys.Oem7 : Key_Listener("¨")
                Case Keys.OemBackslash : Key_Listener(">")
                Case Keys.Oemcomma : Key_Listener(";")
                Case Keys.OemMinus : Key_Listener("_")
                Case Keys.OemOpenBrackets : Key_Listener("?")
                Case Keys.OemPeriod : Key_Listener(":")
                Case Keys.Oemplus : Key_Listener("*")
                Case Keys.OemQuestion : Key_Listener("Ç")
                Case Keys.Oemtilde : Key_Listener("Ñ")
                Case Else : Key_Listener("")
            End Select

        Case Else

            If Key.ToString.Length = 1 Then ' Single alpha key

                If Control.IsKeyLocked(Keys.CapsLock) Or Control.ModifierKeys = Keys.Shift Then
                    Key_Listener(Key.ToString.ToUpper)
                Else
                    Key_Listener(Key.ToString.ToLower)
                End If

            Else

                Select Case Key ' Single special key 
                    Case Keys.Add : Key_Listener("+")
                    Case Keys.Back : Key_Listener("{BackSpace}")
                    Case Keys.D0 : Key_Listener("0")
                    Case Keys.D1 : Key_Listener("1")
                    Case Keys.D2 : Key_Listener("2")
                    Case Keys.D3 : Key_Listener("3")
                    Case Keys.D4 : Key_Listener("4")
                    Case Keys.D5 : Key_Listener("5")
                    Case Keys.D6 : Key_Listener("6")
                    Case Keys.D7 : Key_Listener("7")
                    Case Keys.D8 : Key_Listener("8")
                    Case Keys.D9 : Key_Listener("9")
                    Case Keys.Decimal : Key_Listener(".")
                    Case Keys.Delete : Key_Listener("{Supr}")
                    Case Keys.Divide : Key_Listener("/")
                    Case Keys.End : Key_Listener("{End}")
                    Case Keys.Enter : Key_Listener("{Enter}")
                    Case Keys.F1 : Key_Listener("{F1}")
                    Case Keys.F10 : Key_Listener("{F10}")
                    Case Keys.F11 : Key_Listener("{F11}")
                    Case Keys.F12 : Key_Listener("{F12}")
                    Case Keys.F2 : Key_Listener("{F2}")
                    Case Keys.F3 : Key_Listener("{F3}")
                    Case Keys.F4 : Key_Listener("{F4}")
                    Case Keys.F5 : Key_Listener("{F5}")
                    Case Keys.F6 : Key_Listener("{F6}")
                    Case Keys.F7 : Key_Listener("{F7}")
                    Case Keys.F8 : Key_Listener("{F8}")
                    Case Keys.F9 : Key_Listener("{F9}")
                    Case Keys.Home : Key_Listener("{Home}")
                    Case Keys.Insert : Key_Listener("{Insert}")
                    Case Keys.Multiply : Key_Listener("*")
                    Case Keys.NumPad0 : Key_Listener("0")
                    Case Keys.NumPad1 : Key_Listener("1")
                    Case Keys.NumPad2 : Key_Listener("2")
                    Case Keys.NumPad3 : Key_Listener("3")
                    Case Keys.NumPad4 : Key_Listener("4")
                    Case Keys.NumPad5 : Key_Listener("5")
                    Case Keys.NumPad6 : Key_Listener("6")
                    Case Keys.NumPad7 : Key_Listener("7")
                    Case Keys.NumPad8 : Key_Listener("8")
                    Case Keys.NumPad9 : Key_Listener("9")
                    Case Keys.Oem1 : Key_Listener("`")
                    Case Keys.Oem5 : Key_Listener("º")
                    Case Keys.Oem6 : Key_Listener("¡")
                    Case Keys.Oem7 : Key_Listener("´")
                    Case Keys.OemBackslash : Key_Listener("<")
                    Case Keys.Oemcomma : Key_Listener(",")
                    Case Keys.OemMinus : Key_Listener(".")
                    Case Keys.OemOpenBrackets : Key_Listener("'")
                    Case Keys.OemPeriod : Key_Listener("-")
                    Case Keys.Oemplus : Key_Listener("+")
                    Case Keys.OemQuestion : Key_Listener("ç")
                    Case Keys.Oemtilde : Key_Listener("ñ")
                    Case Keys.PageDown : Key_Listener("{AvPag}")
                    Case Keys.PageUp : Key_Listener("{RePag}")
                    Case Keys.Space : Key_Listener(" ")
                    Case Keys.Subtract : Key_Listener("-")
                    Case Keys.Tab : Key_Listener("{Tabulation}")
                    Case Else : Key_Listener("")
                End Select

            End If

    End Select

End Sub

Public Sub Key_Listener(ByVal key As String)

    If Auto_Backspace_Key AndAlso key = "{BackSpace}" Then ' Delete character
        RichTextBox1.Text = RichTextBox1.Text.Substring(0, RichTextBox1.Text.Length - 1)
    ElseIf Auto_Enter_Key AndAlso key = "{Enter}" Then ' Insert new line
        RichTextBox1.Text += ControlChars.NewLine
    ElseIf Auto_Tab_Key AndAlso key = "{Tabulation}" Then ' Insert Tabulation
        RichTextBox1.Text += ControlChars.Tab
    ElseIf No_F_Keys AndAlso key.StartsWith("{F") Then ' Ommit F Keys
    Else ' Print the character
        RichTextBox1.Text += key
    End If

End Sub

#End Region
4

1 回答 1

2

永远无法编写将虚拟键转换为键入键的正确低级键盘钩子。键盘状态和活动键盘布局是拥有前台窗口的进程的属性。从来没有实现钩子的过程。

特别是键盘状态会出错,你不知道进程的键盘逻辑状态是否有 shift、alt、control 和 Windows 键处于活动状态。当程序接收到键盘事件时记录该状态。使用变音符号的语言的键盘布局特别是死键的状态,即您键入的那些以使下一个键入的字母具有重音符号。此键盘状态是每个进程的状态,不能从另一个进程中检索。它只能在进程本身的 GetKeyboardState() 函数中发现。对于活动键盘布局,GetKeyboardLayout() 函数非常相似。语言栏允许进程使用不同的布局。

只有当您使用 WH_KEYBOARD 挂钩时,它才能 100% 正确工作。它需要一个可以注入其他进程的 DLL。SetWindowsHookEx() 的第三个参数。这可确保 GetKeyboardState 和 GetKeyboardLayout 返回准确的信息。您不能在 VB.NET 中编写这样的 DLL,您注入的进程不会加载 CLR 来执行托管代码。需要像 C、C++ 或 Delphi 这样的语言,这些语言的运行时支持要求非常低。这通常是项目逐渐消失的地方。不仅仅是因为运行时注入问题,在 64 位操作系统和 UAC 上调试此类代码和处理进程的位数都是令人头疼的问题。

您可以通过使用 GetAsyncKeyState() 来获取修饰键的状态。除了注入的 DLL 之外,没有解决死键的方法。这不是一个有用的答案,它只是解释了为什么你永远不能让它在 vb.net 中完全可靠地工作。

于 2013-06-03T15:12:27.850 回答