The CKeyboard Class Implementation


CKeyboard is a simple wrapper for the Windows functions GetKeyboardState and SetKeyboardState. These two functions work on an array of 256 bytes that represent the 256 keys on the keyboard. What? Your keyboard doesn’t have 256 keys? Well, if it did, you could put them in this array. Windows is just leaving room for expansion.


The Byte type in Visual Basic makes key handling easy. The CKeyboard property procedures share the following private array:

Private abKeys(0 To 255) As Byte

To read the state of a key, you get all the key states and then read the one you want, using the virtual key code as an index into the key array:

Property Get CapsState() As Boolean
' Get toggled and pressed state of all keys
GetKeyboardState abKeys(0)
' Check low bit for state
CapsState = abKeys(VK_CAPITAL) And 1
End Property

The low bit of each toggle key indicates whether the toggle is on or off. Notice how we pass the array to GetKeyboardState by passing the first element. “Arrays” in Chapter 2 explains how this works.


Virtual key codes in the Visual Basic format (such as vbKeyInsert) are in the VBRUN type library under KeyCodeConstants. You can also find them with Windows-style names (VK_DELETE) in the Windows API type library under UserConst.


To change the state of a key, you must read in the current key states, modify the key in the array, and write the result back with SetKeyboardState:

Property Let CapsState(fCapsStateA As Boolean)
' Get toggled and pressed state of all keys
GetKeyboardState abKeys(0)
' Set low bit to new pressed state
If fCapsStateA Then
abKeys(VK_CAPITAL) = abKeys(VK_CAPITAL) Or 1
Else
abKeys(VK_CAPITAL) = abKeys(VK_CAPITAL) And &HFE
End If
' Store changed array
SetKeyboardState abKeys(0)
End Property

The KeyPressed property works the same way, except that it will check any key. The pressed state of a key is stored in the high bit, and you must provide the virtual key code of the key. Here’s the Property Get procedure:

Property Get KeyPressed(iKey As Integer) As Boolean
' Get toggled and pressed state of all keys
GetKeyboardState abKeys(0)
' Check high bit for state
KeyPressed = abKeys(iKey) And &H80
End Property

A Property Get procedure with an argument makes the property look like an array element, as described in the sidebar “Property Arrays” in Chapter 4. Check out the KeyPressed Property Let in KEYBOARD.CLS.


Here’s how you can use KeyPressed to read keys:

fShift = Keyboard.KeyPressed(VK_CONTROL) ' Is Ctrl key down?

You can also press a key programmatically:

Keyboard.KeyPressed(VK_F1) = True   ' Press F1
DoEvents
Keyboard.KeyPressed(VK_F1) = False ' Release F1

So why didn’t I use the KeyPressed property to press the insert key in the XEditor control? Because KeyPressed puts the key into the system keyboard queue, and the system determines what window to route the key to. I wanted to send the key to a specific window.