如何在VB.NET中从外部应用程序触发keydown和keyup事件?避免使用sendkeys方法



我正试图使用vb.net开发一个击键宏程序,该程序将记录keydownkeyup事件等击键,然后在主程序后台运行时在任何地方播放。到目前为止,我已经成功地捕捉到了击键并存储了这些笔划。但我面临的问题是,在播放那些存储的击键时。我无法从任何外部程序启动KeyDown和KeyUp事件。我也尝试过SendKeys方法,但它无法分别区分KeyDown和KeyUp。在此情况下提供帮助将不胜感激。

仅在父程序中可访问的KeyDown事件

Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
'MessageBox.Show(e.KeyCode)
'bla bla bla
End Sub

'使用SendKeys,但无法区分KeyDown和KeyUp

Private Function AutoSendKey(ByVal keystroke As String, ByVal delay As Integer)     
System.Threading.Thread.Sleep(delay)
My.Computer.Keyboard.SendKeys(keystroke, True)
End Function

我需要一种从外部应用程序触发KeyDown和KeyUp事件的方法。提前感谢

我只是在寻找同样的东西,发现了一些资源,然后设法将其集成到一个应用程序中:

基本上,除非你在驱动程序级别上使用某种拦截,否则这将起作用,在这种情况下,拦截应用程序拦截的键盘对窗口来说是不可见的,因此这个脚本是无用的。

除此之外,这是我的代码。此代码针对.NET 5.0 进行了优化

首先创建KeyboardHook.vb类:

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 Key)
Public Shared Event KeyUp(ByVal Key As Key)
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
Dim aKey = KeyInterop.KeyFromVirtualKey(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode)
RaiseEvent KeyDown(aKey)
Case WM_KEYUP, WM_SYSKEYUP
Dim aKey = KeyInterop.KeyFromVirtualKey(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode)
RaiseEvent KeyUp(aKey)
End Select
End If
Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)
End Function
Public Sub New()
HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Diagnostics.Process.GetCurrentProcess().MainModule.BaseAddress, 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

现在使用KeyboardHook事件处理程序创建您自己的类/用户控件。

对我来说,这是一个简单的OnScreenKBView.xaml,它的文本块具有绑定到视图模型类属性的文本属性,该属性显示按下和释放了哪些键,以及同时按下了多少键:

<Grid>
<Border Background="Red" >
<StackPanel Orientation="Vertical">
<TextBlock Text="KeyDown:"/>
<TextBlock Text="{Binding keyDown, Converter={StaticResource KeyToStringVC}}"/>
<TextBlock Text="KeyUp:"/>
<TextBlock Text="{Binding keyUp, Converter={StaticResource KeyToStringVC}}"/>
<TextBlock Text="TotalKeysPressed at the same time:"/>
<TextBlock Text="{Binding keysPressed, Converter={StaticResource ListToCountVC}}"/>
<TextBlock Text="KeysPressed:"/>
<TextBlock Text="{Binding keysPressed, Converter={StaticResource ListToStringVC}}" TextWrapping="Wrap"/>
</StackPanel>

</Border> 
</Grid>

注意,我有几个值转换器KeyToStringVCListToStringVCListToCountVC

KeyToStringVC:

Return value.ToString

ListToCountVC:

If Not value Is Nothing Then
Return value.count
Else
Return Nothing
End If

ListToStringVC:

Dim kl As ObservableCollection(Of Key) = value
Dim str As String = Nothing
If Not kl Is Nothing Then
For Each item In kl
str += item.ToString & "; "
Next
End If
Return str

如果你不知道如何使用/创建价值转换器,你将不得不查找。

OnScreenKBView.xaml:背后的VB代码

Imports System.Collections.ObjectModel
Public Class OnScreenKBView

Private WithEvents kbHook As New KeyboardHook
Private viewModel As OnScreenKBViewModel

Private Sub kbHook_KeyDown(ByVal Key As Key) Handles kbHook.KeyDown
viewModel.keyDown = Key

'check if list already has the key in it
Dim hasKey As Boolean = False
If Me.viewModel.keysPressed.Contains(Key) Then
hasKey = True
End If
If Not hasKey Then
Me.viewModel.keysPressed.Add(Key)
Dim localCol = Me.viewModel.keysPressed
Dim newCol = New ObservableCollection(Of Key)(From i In localCol Select i)
Me.viewModel.keysPressed = newCol
End If
End Sub
Private Sub kbHook_KeyUp(ByVal Key As Key) Handles kbHook.KeyUp
viewModel.keyUp = Key
Me.viewModel.keysPressed.Remove(Key)
Dim localCol = Me.viewModel.keysPressed
Dim newCol = New ObservableCollection(Of Key)(From i In localCol Select i)
Me.viewModel.keysPressed = newCol
End Sub
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.viewModel = Application.Current.MainWindow.DataContext
End Sub
End Class

我希望这会给你一些想法。如果你需要整个项目,我在github上有它,但它是私人的,我可以为你压缩它。

Lil演示:GIF此处

Imports System.Runtime.InteropServices
Imports System.Windows.Forms 'for the keys. enumeration
Public Module SendWinKey
Const KEYEVENTF_KEYDOWN As Integer = &H0
Const KEYEVENTF_KEYUP As Integer = &H2

Declare Sub keybd_event Lib "User32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As UInteger, ByVal dwExtraInfo As UInteger)
Public Sub Main()    
keybd_event(CByte(Keys.LWin), 0, KEYEVENTF_KEYDOWN, 0) 'press the left Win key down
keybd_event(CByte(Keys.R), 0, KEYEVENTF_KEYDOWN, 0) 'press the R key down
keybd_event(CByte(Keys.R), 0, KEYEVENTF_KEYUP, 0) 'release the R key
keybd_event(CByte(Keys.LWin), 0, KEYEVENTF_KEYUP, 0) 'release the left Win key
End Sub
End Module

正如你所看到的,这真的很简单。

最新更新