Monday, February 22, 2010

Simulating Keyboard Input with C#

I visited a potential client who is using a barcode reader-based timekeeping system. Well, actually, the barcode reader is just configured as a keyboard wedge. So from the application's point of view, it just looks like a the user is typing his ID number on the keyboard. This prospect is interested in shifting into a fingerprint-based biometric timekeeping system. But because of their complex business rules, we looked at ways to implement the biometric identification as an adjunct to the existing system.

So the challenge is -- how do I capture a fingerprint; determine its associated ID; then simulate a keyboard input to their existing keyboard-based 3rd application so that no changes need to be made on their end. The same business rules would apply. Since their program is keyboard-based instead of serial or USB, that greatly simplified matters. I recall reading of a Windows API to send characters to the 'keyboard' device to make it appear as if somebody inputed something on the keyboard. But the extra challenge here is to be able to send those virtual keypresses into another running application.

A little Google search for examples in C# yielded some answers. The SendKeys class in .NET Framework already encapsulated the Windows API calls to do this. I just need to first invoke SetForegroundWindow() to transfer control to the application that I want to send the keys to, then SendKeys does the rest. There is no existing encapsulation of the SetForegroundWindow() API call in .NET so I had to use the platform invoke (pinvoke) method. This is easily done with the ff. declaration:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);

As can be seen from the declaration, SetForegroundWindow() requires a native Window handle as a parameter. One way of getting a window handle is by enumerating the processes running in a system. The process object's MainWindowHandle property is what we need to pass to the API.

Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
SetForegroundWindow(pFoundWindow);

System.Threading.Thread.Sleep(50);
System.Windows.Forms.SendKeys.SendWait("Hello world");
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
}

The above example assumes there's a Notepad running on the system. This program sends the text "Hello world" to the notepad's window. Based on my tests, the Sleep call is important as it takes a split second for SetForegroundWindow() to switch windows. Without the needed pause, the first few characters of the keys to send are missed.

1 comment:

nuva said...

This is a verry helpfull post, but I was wondering, is it possible to call multiple keys at once.

For example:
CTRL+S