我正在尝试编写一个简单的桌面应用程序来捕获用户输入并将其写入文本文件。我已经弄清楚如何捕获鼠标移动和鼠标点击。我正在将此信息写入文本文件。然后我使用该应用程序重新创建使用输入设备执行的操作。基本上我正在尝试编写自己的宏记录器。
我不确定在捕获键盘输入时我做错了什么。我正在使用以下代码,但当我用键盘键入时它没有被执行。因此,它不会将键盘输入写入文本文件。我忽略了什么?
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
如果您想在自己的表单之外捕获键盘输入,则需要安装全局键盘钩子。
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。