未捕获键盘输入

问题描述 投票:0回答:1

我正在尝试编写一个简单的桌面应用程序来捕获用户输入并将其写入文本文件。我已经弄清楚如何捕获鼠标移动和鼠标点击。我正在将此信息写入文本文件。然后我使用该应用程序重新创建使用输入设备执行的操作。基本上我正在尝试编写自己的宏记录器。

我不确定在捕获键盘输入时我做错了什么。我正在使用以下代码,但当我用键盘键入时它没有被执行。因此,它不会将键盘输入写入文本文件。我忽略了什么?

Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    Try
        ' Add key down event to recorded actions
        Dim key As Keys = e.KeyCode
        recordedActions.Add(String.Format("KeyDown,{0}", key))
    Catch ex As Exception
        MessageBox.Show("Error capturing key down: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub

Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles MyBase.KeyUp
    Try
        ' Add key up event to recorded actions
        Dim key As Keys = e.KeyCode
        recordedActions.Add(String.Format("KeyUp,{0}", key))
    Catch ex As Exception
        MessageBox.Show("Error capturing key up: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub
windows vb.net visual-studio-2013 desktop-application
1个回答
0
投票

如果您想在自己的表单之外捕获键盘输入,则需要安装全局键盘钩子。

Imports System.ComponentModel
Imports System.Runtime.InteropServices

Public Class Form1
    Dim kh As New KeyboardHook
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        kh.Hook()
    End Sub
    Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
        kh.Unhook()
    End Sub
End Class

Public Class KeyboardHook : Implements IDisposable

    Public Delegate Function HookCallBack(nCode As Integer, wParam As IntPtr, lParam As IntPtr) As Integer

    <DllImport("Kernel32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function GetModuleHandle(ByVal ModuleName As String) As IntPtr : End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function SetWindowsHookEx(idHook As Integer, HookProc As HookCallBack,
           hInstance As IntPtr, ThreadId As Integer) As IntPtr : End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function CallNextHookEx(hHook As IntPtr, nCode As Integer,
           wParam As IntPtr, lParam As IntPtr) As Integer : End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function UnhookWindowsHookEx(hHook As IntPtr) 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 HookHandle As IntPtr = IntPtr.Zero

    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 Function KeyProc(
        ByVal nCode As Integer,
        ByVal wParam As IntPtr,
        ByVal lParam As IntPtr) As Integer

        If nCode < 0 OrElse nCode <> HC_ACTION Then Return CallNextHookEx(HookHandle, nCode, wParam, lParam)

        Dim hs As KBDLLHOOKSTRUCT = Marshal.PtrToStructure(Of KBDLLHOOKSTRUCT)(lParam)
        Dim key As Keys = CType(hs.vkCode, Keys)
        Dim scan = hs.scanCode

        Select Case wParam.ToInt32
            Case WM_KEYDOWN
                Debug.Print($"{key} keydown {scan}")
            Case WM_KEYUP
                Debug.Print($"{key} keyup  {scan}")
            Case WM_SYSKEYDOWN 'when alt is down it will use these isntead
                Debug.Print($"{key} syskeydown {scan}")
            Case WM_SYSKEYUP
                Debug.Print($"{key} syskeyup {scan}")
        End Select

        Return CallNextHookEx(HookHandle, nCode, wParam, lParam)
    End Function

    Private mhCallBack As HookCallBack = New HookCallBack(AddressOf KeyProc)
    Private disposedValue As Boolean

    Public Sub Hook()
        If HookHandle <> IntPtr.Zero Then Exit Sub
        HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, mhCallBack,
            GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0)
        If HookHandle = IntPtr.Zero Then Throw New System.Exception("KeyboardHook failed")
    End Sub

    Public Sub Unhook()
        If HookHandle <> IntPtr.Zero Then
            UnhookWindowsHookEx(HookHandle)
            HookHandle = IntPtr.Zero
        End If
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects)
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override finalizer
            Unhook()
            ' TODO: set large fields to null
            disposedValue = True
        End If
    End Sub

    ' TODO: override finalizer only if 'Dispose(disposing As Boolean)' has code to free unmanaged resources
    Protected Overrides Sub Finalize()
        ' Do not change this code. Put cleanup code in 'Dispose(disposing As Boolean)' method
        Dispose(disposing:=False)
        MyBase.Finalize()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code. Put cleanup code in 'Dispose(disposing As Boolean)' method
        Dispose(disposing:=True)
        GC.SuppressFinalize(Me)
    End Sub

End Class

请注意,安装全局键盘挂钩将被许多(如果不是全部)病毒扫描程序标记为可疑。

如果您需要获取更多信息而不仅仅是简单的向上/向下,请阅读https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct

© www.soinside.com 2019 - 2024. All rights reserved.