SendKeys using the API

SendKeys Demonstration

VB provides the SendKeys command which is supposed to create key events in the focus control. However, in practice SendKeys sometimes doesn't work correctly, it is missing some keys and doesn't offer much flexibility in controlling the sequence of key strokes that gets sent. This article demonstrates using the Win32 keybd_event API call to circumvent these limitations.

About keybd_event

The keybd_event functions synthesizes keystrokes; it is essentially the same routine that is called by a keyboard driver to generate real key events. Although the function has been superceded in NT and more modern 9x versions by SendInput it is still implemented on all systems and works well in most cases.

keybd_event itself takes two useful parameters; the virtual key code to emulate a keystroke for and a flag indicating whether the event is a key up or key down event.

There are two ways of setting the virtual key code. Firstly, you can use the virtual key constant directly. VB contains constants for most of the virtual key codes in the KeyCodeConstants enumeration, however, there are a number of missing keys such as the right mouse button key on the keyboard. Secondly, you can use the API VkKeyScan function to get the key code for a particular character.

Bringing this together, we can write simple functions to create key down and key up events:

Private Declare Sub keybd_event Lib "user32" ( _
   ByVal bVk As Byte, ByVal bScan As Byte, _
   ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const KEYEVENTF_EXTENDEDKEY = &H1
Private Const KEYEVENTF_KEYUP = &H2

Private Declare Function GetVersion Lib "kernel32" () As Long
Private Declare Function VkKeyScan Lib "user32" Alias "VkKeyScanA" ( _
   ByVal cChar As Byte) As Integer
Private Declare Function VkKeyScanW Lib "user32" ( _
   ByVal cChar As Integer) As Integer

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)


Public Sub KeyDown(ByVal vKey As KeyCodeConstants)
   keybd_event vKey, 0, KEYEVENTF_EXTENDEDKEY, 0
End Sub

Public Sub KeyUp(ByVal vKey As KeyCodeConstants)
   keybd_event vKey, 0, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0
End Sub

Public Function KeyCode(ByVal sChar As String) As KeyCodeConstants
Dim bNt As Boolean
Dim iKeyCode As Integer
Dim b() As Byte
Dim iKey As Integer
Dim vKey As KeyCodeConstants
Dim iShift As ShiftConstants

   ' Determine if we have Unicode support or not:
   bNt = ((GetVersion() And &H80000000) = 0)
   
   ' Get the keyboard scan code for the character:
   If (bNt) Then
      b = sChar
      CopyMemory iKey, b(0), 2
      iKeyCode = VkKeyScanW(iKey)
   Else
      b = StrConv(sChar, vbFromUnicode)
      iKeyCode = VkKeyScan(b(0))
   End If
   
   KeyCode = (iKeyCode And &HFF&)

End Function

Being able to independently make key down and key up calls can make using send keys more flexible. However, you should be careful to ensure you pair key down and up calls, particularly with the control, alt and shift keys otherwise your computer can become hard to use!

Demonstration Application

The demonstration application takes the code given above and wraps an implementation of the VB SendKeys parser around it in a class called cSendKeys. This class can be used almost as a drop in replacement for the VB SendKeys function, the only difference being that you need to create an instance of the class and use it to call the SendKeys method. The application uses this class to perform a variety of key operations on it's own form (although practically you would probably use this code to automate another window).