Link to home
Start Free TrialLog in
Avatar of arthurh88
arthurh88

asked on

How do I send a keypress hold down/up to another application?

I want to send some commands to minecraft on windows 10...specifically to hold down the 'W' key so that my game will move forward for X miliseconds, then release, then send a spacebar to "Jump".  Using sendkeys doesn't work so well for me because they send single keystrokes and I'd like to hold keys down.  How can I do this?  I use vb.net 2017.  Thank you!
ASKER CERTIFIED SOLUTION
Avatar of Thomas Zucker-Scharff
Thomas Zucker-Scharff
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Keyboard input is controlling not by Windows but with keyboard controller, which communicates with windows using low-level driver. When receiving keyboard signal form driver, windows send WM_KEYDOWN message to active window following by WM_CHAR message. If user release button within hardware delay (usually 31 ms) windows send WM_KEYUP message. If not - windows repeats WM_KEYDOWN and WM_CHAR messages. Delay values are stored in registry under HKCU\Control Panel\Keyboard key. You can post these messages using timers:
Imports System.Runtime.InteropServices
'API calls:
   <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Private Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
    End Function
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
    End Function
    Const VK_SPACE = &H20
    Const VK_W = &H57
    Const WM_KEYDOWN = &H100
    Const WM_KEYUP = &H101
    Const WM_CHAR = &H102
'Fields:
    Private keyboardDelay, keyboardSpeed,
        timesPressing, counter As Integer,
        hwnd As IntPtr = IntPtr.Zero,
        startTimer, repeatTimer As Timer
'Read registry values:
   Private Sub InitKeyboard()
        Using key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Control Panel\Keyboard")
            keyboardDelay = 1
            Integer.TryParse(key.GetValue("KeyboardDelay", "1").ToString, keyboardDelay)
            keyboardSpeed = 31
            Integer.TryParse(key.GetValue("KeyboardSpeed", "31").ToString, keyboardSpeed)
        End Using
    End Sub
'Call (winHandle is target window handle, times - how many times press (hold) key)
    Private Sub SendKeysInput(winHandle As IntPtr, times As Integer)
        hwnd = winHandle
        timesPressing = times
        counter = 0
        startTimer = New System.Windows.Forms.Timer With {.Interval = keyboardSpeed}
        AddHandler startTimer.Tick, AddressOf startTimer_Tick
        startTimer.Start()
    End Sub
'Timers:
    Private Sub repeatTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
        PostMessage(hwnd, WM_KEYDOWN, VK_W, 0)
        PostMessage(hwnd, WM_CHAR, VK_W, 0)
        counter += 1
'press W n times
        If counter > timesPressing Then
            Dim timer As Timer = TryCast(sender, Timer)
            timer.Stop()
'final - release W key and press Space
            SendMessage(hwnd, WM_KEYUP, VK_W, 0)
            PostMessage(hwnd, WM_KEYDOWN, VK_SPACE, 0)
            SendMessage(hwnd, WM_KEYUP, VK_SPACE, 0)
        End If
    End Sub
'First time - press W key and start repeat timer
    Private Sub startTimer_Tick(ByVal sender As Object, ByVal eventArgs As EventArgs)
        Dim timer As Timer = TryCast(sender, Timer)
        timer.Stop()
        PostMessage(hwnd, WM_KEYDOWN, VK_W, 0)
        PostMessage(hwnd, WM_CHAR, VK_W, 0)
        repeatTimer = New System.Windows.Forms.Timer
        repeatTimer.Interval = keyboardDelay
        AddHandler repeatTimer.Tick, AddressOf repeatTimer_Tick
        repeatTimer.Start()
    End Sub

Open in new window