Simulating Keyboard Keystroke Sequences

Keyboard

I recently published an article and YouTube video about Speech Recognition

If you  watch the YouTube video I demonstrate simulating keyboard keystroke sequences via the keybd_event API to enable/disable the Windows Speech Recognition application.

I thought it deserved its own posting, so here it is!

The first thing I can hear some of you saying to me is why not simply use SendKeys?  In the case I was working on, SendKeys was not an option because it does support the keystroke I required, ie the ‘Windows’ key.

Below are the basic VBA keybd_event API declarations/constants.

#If Win64 Then
    Public Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As LongPtr)
#Else
    Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
#End If

Public Const KEYEVENTF_EXTENDEDKEY = &H1
Public Const KEYEVENTF_KEYUP = &H2

' Virtual Keys, Standard Set
Public Const VK_LBUTTON = &H1
Public Const VK_RBUTTON = &H2
Public Const VK_CANCEL = &H3
Public Const VK_MBUTTON = &H4             '  NOT contiguous with L RBUTTON

Public Const VK_BACK = &H8
Public Const VK_TAB = &H9

Public Const VK_CLEAR = &HC
Public Const VK_RETURN = &HD

Public Const VK_SHIFT = &H10
Public Const VK_CONTROL = &H11
Public Const VK_MENU = &H12 '*** ALT ***
Public Const VK_PAUSE = &H13
Public Const VK_CAPITAL = &H14

Public Const VK_ESCAPE = &H1B

Public Const VK_SPACE = &H20
Public Const VK_PRIOR = &H21
Public Const VK_NEXT = &H22
Public Const VK_END = &H23
Public Const VK_HOME = &H24
Public Const VK_LEFT = &H25
Public Const VK_UP = &H26
Public Const VK_RIGHT = &H27
Public Const VK_DOWN = &H28
Public Const VK_SELECT = &H29
Public Const VK_PRINT = &H2A
Public Const VK_EXECUTE = &H2B
Public Const VK_SNAPSHOT = &H2C
Public Const VK_INSERT = &H2D
Public Const VK_DELETE = &H2E
Public Const VK_HELP = &H2F

' VK_0 thru VK_9 are the same as their ASCII equivalents: '0' thru '9'
Public Const VK_0 = &H30
Public Const VK_1 = &H31
Public Const VK_2 = &H32
Public Const VK_3 = &H33
Public Const VK_4 = &H34
Public Const VK_5 = &H35
Public Const VK_6 = &H36
Public Const VK_7 = &H37
Public Const VK_8 = &H38
Public Const VK_9 = &H39
' VK_A thru VK_Z are the same as their ASCII equivalents: 'A' thru 'Z'
Public Const VK_A = &H41
Public Const VK_B = &H42
Public Const VK_C = &H43
Public Const VK_D = &H44
Public Const VK_E = &H45
Public Const VK_F = &H46
Public Const VK_G = &H47
Public Const VK_H = &H48
Public Const VK_I = &H49
Public Const VK_J = &H4A
Public Const VK_K = &H4B
Public Const VK_L = &H4C
Public Const VK_M = &H4D
Public Const VK_N = &H4E
Public Const VK_O = &H4F
Public Const VK_P = &H50
Public Const VK_Q = &H51
Public Const VK_R = &H52
Public Const VK_S = &H53
Public Const VK_T = &H54
Public Const VK_U = &H55
Public Const VK_V = &H56
Public Const VK_W = &H57
Public Const VK_X = &H58
Public Const VK_Y = &H59
Public Const VK_Z = &H5A

Public Const VK_NUMPAD0 = &H60
Public Const VK_NUMPAD1 = &H61
Public Const VK_NUMPAD2 = &H62
Public Const VK_NUMPAD3 = &H63
Public Const VK_NUMPAD4 = &H64
Public Const VK_NUMPAD5 = &H65
Public Const VK_NUMPAD6 = &H66
Public Const VK_NUMPAD7 = &H67
Public Const VK_NUMPAD8 = &H68
Public Const VK_NUMPAD9 = &H69
Public Const VK_MULTIPLY = &H6A
Public Const VK_ADD = &H6B
Public Const VK_SEPARATOR = &H6C
Public Const VK_SUBTRACT = &H6D
Public Const VK_DECIMAL = &H6E
Public Const VK_DIVIDE = &H6F
Public Const VK_F1 = &H70
Public Const VK_F2 = &H71
Public Const VK_F3 = &H72
Public Const VK_F4 = &H73
Public Const VK_F5 = &H74
Public Const VK_F6 = &H75
Public Const VK_F7 = &H76
Public Const VK_F8 = &H77
Public Const VK_F9 = &H78
Public Const VK_F10 = &H79
Public Const VK_F11 = &H7A
Public Const VK_F12 = &H7B
Public Const VK_F13 = &H7C
Public Const VK_F14 = &H7D
Public Const VK_F15 = &H7E
Public Const VK_F16 = &H7F
Public Const VK_F17 = &H80
Public Const VK_F18 = &H81
Public Const VK_F19 = &H82
Public Const VK_F20 = &H83
Public Const VK_F21 = &H84
Public Const VK_F22 = &H85
Public Const VK_F23 = &H86
Public Const VK_F24 = &H87

Public Const VK_NUMLOCK = &H90
Public Const VK_SCROLL = &H91

'   VK_L VK_R - left and right Alt, Ctrl and Shift virtual keys.
'   Used only as parameters to GetAsyncKeyState() and GetKeyState().
'   No other API or message will distinguish left and right keys in this way.
'  /
Public Const VK_LSHIFT = &HA0
Public Const VK_RSHIFT = &HA1
Public Const VK_LCONTROL = &HA2
Public Const VK_RCONTROL = &HA3
Public Const VK_LMENU = &HA4
Public Const VK_RMENU = &HA5

Public Const VK_ATTN = &HF6
Public Const VK_CRSEL = &HF7
Public Const VK_EXSEL = &HF8
Public Const VK_EREOF = &HF9
Public Const VK_PLAY = &HFA
Public Const VK_ZOOM = &HFB
Public Const VK_NONAME = &HFC
Public Const VK_PA1 = &HFD
Public Const VK_OEM_CLEAR = &HFE

'Not in listing, added manually
Public Const VK_LWIN = &H5B    '0x5B - &H5B - 91

Normally, to use the above you need to perform 2 calls for every keystroke, one to press the key (down) and one to release the key (up). Thus, To simulate pressing CTRL + WIN, we would end up doing:

    Call keybd_event(VK_CONTROL, 0, 0, 0) 'Key press
    Call keybd_event(VK_LWIN, 0, 0, 0) 'Key press
    Call keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0) 'Key release
    Call keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0) 'Key release

Finding this tedious to always do for various projects, I had already created the following procedure which can be called with a single line of code to simulate any number of keystrokes!

'---------------------------------------------------------------------------------------
' Procedure : TypeSequence
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Simulate a sequence of keystroke(s)
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd     : keybd_event API and constant declarations
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' aKeyStrokes   list of keystroke constant to emulate
'
' Usage:
' ~~~~~~
' Call TypeSequence(VK_LWIN, VK_R) 'Open Run Dialog
' Call TypeSequence(VK_LWIN, VK_E) 'Open Windows Explorer
' Call TypeSequence(VK_LWIN) 'Open Windows Start Menu
' Call TypeSequence(VK_CONTROL, VK_LWIN) 'Toggle Speech Recognition On/Off
'-----------------------------------------------
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2024-01-29              Initial Public Release
'---------------------------------------------------------------------------------------
Sub TypeSequence(ParamArray aKeyStrokes() As Variant)
    On Error GoTo Error_Handler
    Dim i                     As Long

    For i = 0 To UBound(aKeyStrokes)
        If IsNumeric(aKeyStrokes(i)) Then _
           Call keybd_event(aKeyStrokes(i), 0, 0, 0)  'Key press
    Next i
    DoEvents
    For i = 0 To UBound(aKeyStrokes)
        If IsNumeric(aKeyStrokes(i)) Then _
           Call keybd_event(aKeyStrokes(i), 0, KEYEVENTF_KEYUP, 0)  'Key release
    Next i
    DoEvents

Error_Handler_Exit:
    On Error Resume Next
    Exit Sub

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: TypeSequence" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Sub

So now, with this procedure, I can emulate pressing CTRL + WIN by simply doing:

Call TypeSequence(VK_CONTROL, VK_LWIN)

4 lines into 1.

Isn’t VBA grand some times!

Here are a couple more examples.

Now I’m not saying this is the best way to do this (shell is better for this), but if you wanted to launch the Windows calculator you could do:

Call TypeSequence(VK_LWIN, VK_R) 'open Run dialog
Call TypeSequence(VK_C, VK_A, VK_L, VK_C) 'Type Calc
Call TypeSequence(VK_RETURN) 'Press Enter/Return to initiate

or if you wanted to launch Windows Quick Assist application, you could do:

Call TypeSequence(VK_LWIN)
Call TypeSequence(VK_Q, VK_U, VK_I, VK_C, VK_K)
Sleep 100 'Give Windows search a chance to bring up the Quick Assist app in the search pane
Call TypeSequence(VK_RETURN)

Note in this case, I needed to add a small delay (Sleep) to allow the Search to bring up the application prior to emulating a Return keystroke to activate the app.

Note
If I had coded either of these without my procedure to simplify things, this would have required a minimum of 14 lines of code. So although the TypeSequence() procedure may seem simple, it truly does simplify our coding lives.

So as you can see, this API can really open up some amazing possibilities. You can even use it to automate menus.

Heck, we could automate the menu in the VBE. Say we wanted to create a routine to compile the current project we could simply do:

Call TypeSequence(VK_MENU, VK_D)
Call TypeSequence(VK_L)

Have fun experimenting!

 

Useful Resources

 

Page History

Date Summary of Changes
2024-01-31 Initial Release