Al is a contributing editor for Dr. Dobb's Sourcebook. You can contact him at http://www.al-williams.com/awc.

Although Borland bills Delphi as a good replacement for Microsoft's Visual Basic (VB), it is not compatible with Visual Basic. Porting a VB program to Delphi (or any other language) is no easy task. Besides the language differences, there are several statements in VB that don't have direct equivalents in other languages. Consider the SendKeys call, for example.
SendKeys is one of those statements that you either don't need or can't live without. Its sole purpose is to send fake keystrokes to the system. You might think this is a simple task. Why not just post WM_CHAR or WM_KEYDOWN messages? There are several problems with this simple approach. Although you can easily fake simple characters to single applications, there is no simple way to send the messages to whatever application is active. Further, you can't affect the state of the keyboard, which many applications check. For example, a program might detect the Esc key, then examine the keyboard state to find out if the Shift or Ctrl keys are down. Posting a simple message won't handle these programs. Unless you are using VB, you clearly need a better way to send keystrokes.
The Win32 API provides a useful function, keybd_event, to address this problem. Win16 has a similar function, but it is designed for use by device drivers and isn't easy to use (see the accompanying text box entitled "The 16-bit Solution"). However, the Win32 version is easy to use from any language.
In this column, I'll present a 32-bit Delphi program that implements a simple keyboard macro program. Almost all of the functionality of the program is encapsulated in a reusable unit that practically duplicates the VB SendKeys functions and syntax (see the accompanying text box entitled "Using SendKeys"). Even if you don't use Delphi, you can easily adapt the techniques this program uses to nearly any programming language.
If you are even more ambitious, you could provide similar mouse capabilities by using mouse_event. Armed with these two functions and a knowledge of Windows hooks, you could implement a very powerful macro recorder, script language, or a sophisticated automatic-test program.
Here's a real life example: I recently wanted a way to advance PowerPoint slides by remote control. I looked at the various presentation remote controls on the market. They all cost over $100 and did things I didn't care about. All I wanted was to advance slides. I bought a $20 wireless doorbell from Radio Shack and wired it to a serial port connector. Then I wrote some simple software to "press" the Enter key (or any other string I designated) when it detected the remote control click. This type of software is simple to write with SendKeys.
Don't forget that virtual key codes are not ASCII characters. There is a virtual key code for each key on the keyboard. For example, there is one virtual key code for the 5 key. There isn't a code for the percent sign that appears above the 5 on the key. When a program sees a WM_KEYDOWN message for the 5 key, it has to examine the state of the Shift key to determine if it is a 5 or a percent sign. Normally, the TranslateMessage function takes care of this logic and generates a WM_CHAR message for the appropriate ASCII character. To simulate a % key, you must simulate a Shift-key press, a 5-key press, a 5-key release, and a Shift-key release.
Of course, you usually don't want to know all of these details just to send an ASCII character. That's why you call the VkKeyScan function. This call converts an ASCII character into a special 32-bit value. The 16 bits at the bottom of this value contain the corresponding virtual key code. The 16 bits at the top contain flags to tell you which of the Shift, Ctrl, or Alt keys should be down for the character. This simplifies handling ASCII characters. Of course, to send a special character (like a cursor key) you can use the special key's virtual key code directly.
The other detail you usually don't care about is the scan code. It turns out that most programs never examine the scan code anyway. However, the scan code is easy to obtain. Simply call MapVirtualKey, passing it the virtual key code and a zero. This returns the correct scan code. Armed with this information, it is easy to call keybd_event for any key.
There are two flags for each modifier key. For example, the Shift-key flags are shift and letshift. The shift flag reflects the state of the Shift key in response to the "+" key. The letshift key, on the other hand, reflects the Shift-key state required to obtain the desired character (the program uses the VkKeyScan function to set letshift). The program clears letshift for each character. Ordinarily, it clears the shift flag, too. However, if you use a parenthesis, the code will be prevented from clearing the shift flag.
Sometimes, keystrokes can come too rapidly. You might send keystrokes to start a program, for example, then send more keystrokes before the program starts. To help prevent this, the SendKeys function delays 50 milliseconds between each keystroke. You can also program custom delays into scripts using the {PAUSE n} command, which causes the script to pause for n milliseconds. (By the way, this is not part of the standard Visual Basic SendKeys command.)
When the script runs, the program minimizes itself into an icon. This helps prevent KeyPlay from sending keystrokes to itself. Running the script is simply a matter of iterating over each line in the TMemo and sending it to the SendKeys function.
File input and output is simple, too. Each TMemo contains a TStrings object (Lines). This TStrings object can read and write ASCII files easily. Just call LoadFromFile to read a file into the TMemo, and SaveToFile to save it back. (The source code for the KeyPlay program are available electronically.)
If you are programming Windows 3.x, you can still use a similar technique to simulate keystrokes. The _keybd_event call is similar to keybd_event. However, it is primarily aimed at device drivers. Therefore, you need to use assembly language to call it, and it isn't declared in any of the header files.
You can find a good article about using this technique under Windows 3.x in "Simulating Keyboard Input Between Programs Requires a (Key)stroke of Genius," by Jeffrey Richter (Microsoft Systems Journal, December 1992), which shows how to use the 16-bit version of this call.
Using SendKeys is quite simple. The SendKeys procedure accepts a string. SendKeys simulates the ordinary characters in this string. The only characters that have special meaning are: +, %, ^, (, ), {, }, and ~. The ~ character stands for the Enter key. The +, ^, and % characters indicate the Shift, Ctrl, and Alt key attributes. For example, to send "DDJ" a Control-A, the Enter key, and an Alt-X, you'd use the string DDJ^A~%X.
If you want a modifier key to apply to more than one character, surround the characters with parenthesis. For example, to send Control-A, Control-B, and Control-C, you might use ^(ABC). Notice that this is different from ^ABC, which would send a Control-A, an ordinary B, and an ordinary C.
The curly brace indicates a special character. If there is one character inside the brace, SendKeys quotes that character (which is usually a special character); for example, {+}{{} sends a plus sign and an open curly brace. You can also use a named key inside curly braces (see Table 1). Only the first three letters of the name are significant and case isn't important. For example, you can use {ENT} instead of {ENTER}, if you like.
Table 1: Named keys. (Only the first three letters are significant, so {DEL}, {DELETE}, and {DELIT} are the same.)
Name Key
{BACKSPACE} Backspace
{BS} Backspace
{BKSP} Backspace
{BREAK} Break
{CAPS} Caps Lock
{DELETE} Delete
{DOWN} Down arrow
{END} End
{ENTER} Enter (same as ~)
{ESCAPE} Escape
{HELP} Help key
{HOME} Home
{INSERT} Insert
{LEFT} Left arrow
{NUMLOCK} Num lock
{PGDN} Page down
{PGUP} Page up
{PRTSCR} Print screen
{RIGHT} Right arrow
{SCROLL} Scroll lock
{TAB} Tab
{UP} Up arrow
{PAUSE} Pause (see text)
You can also include a space and a repeat count inside the curly braces. For example, {A 10}{LEFT 9} sends ten letter "A"s and nine Left-Arrow keys. The only extension that this code makes to the standard Visual Basic statement is the {PAUSE} keyword. You always provide a repeat count with this keyword. It causes the script to pause for the specified number of milliseconds.
One other difference between the Visual Basic SendKeys statement and the implementation in this article is how the procedure continues. With Visual Basic, you can include a flag that prevents SendKeys from returning before the system processes the keys. In this implementation, SendKeys always returns after sending the keystrokes. There is no way to tell if an application has processed those keys when the procedure returns.