Emulating keystrokes in Windows
One of the core functions in Shoot, a voice command program I wrote a couple of years ago, is the ability to emulate key presses programmatically.
There is a fairly straightforward way to do this in Windows 2000/XP, via the SendInput Win32 function. In Windows 98/ME things are somewhat more complicated: SendInput inserts the keystrokes in the Windows event queue, but DirectInput bypasses it and hooks directly into the keyboard driver.
As a result, any keystroke sent via SendInput is completely ignored by DirectInput applications. Pretty much every modern Windows-based game uses DirectInput, the main target for Shoot. The solution is to implement a Virtual Device Driver (VxD) that inserts the keystrokes directly into the keyboard driver buffer, and that is what I ended up doing for Shoot.
Here's how to use the SendInput function from C#. I stripped all the Win98/ME-related code. Not only would it make the code below significantly more complex, but it's probably useless by now. Who's using Win 98, anyway? Not even Microsoft supports it anymore :)
There is a fairly straightforward way to do this in Windows 2000/XP, via the SendInput Win32 function. In Windows 98/ME things are somewhat more complicated: SendInput inserts the keystrokes in the Windows event queue, but DirectInput bypasses it and hooks directly into the keyboard driver.
As a result, any keystroke sent via SendInput is completely ignored by DirectInput applications. Pretty much every modern Windows-based game uses DirectInput, the main target for Shoot. The solution is to implement a Virtual Device Driver (VxD) that inserts the keystrokes directly into the keyboard driver buffer, and that is what I ended up doing for Shoot.
Here's how to use the SendInput function from C#. I stripped all the Win98/ME-related code. Not only would it make the code below significantly more complex, but it's probably useless by now. Who's using Win 98, anyway? Not even Microsoft supports it anymore :)
[StructLayout(LayoutKind.Sequential)]
private struct KEYBOARD_INPUT {
public uint type;
public ushort vk;
public ushort scanCode;
public uint flags;
public uint time;
public uint extrainfo;
public uint padding1;
public uint padding2;
}
[DllImport("User32.dll")]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst=1)] KEYBOARD_INPUT[] input, int structSize);
void press(int scanCode)
{
sendKey(scanCode, true);
}
void release(int scanCode)
{
sendKey(scanCode, false);
}
private void sendKey(int scanCode, bool press)
{
KEYBOARD_INPUT[] input = new KEYBOARD_INPUT[1];
input[0] = new KEYBOARD_INPUT();
input[0].type = INPUT_KEYBOARD;
input[0].flags = KEY_SCANCODE;
if ((scanCode & 0xFF00) == 0xE000) { // extended key?
input[0].flags |= KEY_EXTENDED;
}
if (press) { // press?
input[0].scanCode = (ushort) (scanCode & 0xFF);
}
else { // release?
input[0].scanCode = scanCode;
input[0].flags |= KEY_UP;
}
uint result = SendInput(1, input, Marshal.SizeOf(input[0]));
if (result != 1) {
throw new Exception("Could not send key: " + scanCode);
}
}
private struct KEYBOARD_INPUT {
public uint type;
public ushort vk;
public ushort scanCode;
public uint flags;
public uint time;
public uint extrainfo;
public uint padding1;
public uint padding2;
}
[DllImport("User32.dll")]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst=1)] KEYBOARD_INPUT[] input, int structSize);
void press(int scanCode)
{
sendKey(scanCode, true);
}
void release(int scanCode)
{
sendKey(scanCode, false);
}
private void sendKey(int scanCode, bool press)
{
KEYBOARD_INPUT[] input = new KEYBOARD_INPUT[1];
input[0] = new KEYBOARD_INPUT();
input[0].type = INPUT_KEYBOARD;
input[0].flags = KEY_SCANCODE;
if ((scanCode & 0xFF00) == 0xE000) { // extended key?
input[0].flags |= KEY_EXTENDED;
}
if (press) { // press?
input[0].scanCode = (ushort) (scanCode & 0xFF);
}
else { // release?
input[0].scanCode = scanCode;
input[0].flags |= KEY_UP;
}
uint result = SendInput(1, input, Marshal.SizeOf(input[0]));
if (result != 1) {
throw new Exception("Could not send key: " + scanCode);
}
}
5 Comments:
Ive worked a lot with this method, trying to use the SendInput and another one for a program a I am working on, with little success at all. I have another method which works on all but DirectX.
I cannot get this one to work anywhere though. Can you send a compilable version of this. and explain more about how you worked with the driver for a solution, as I saw that mentioned elsewhere as well.
James falazar@yahoo.com
is that possible to put your vxd source inline ?
Hi, Could you please send me the values for INPUT_KEYBOARD, KEY_EXTENDED, KEY_UP?
Thanks,
Emil.Aguinaldo@AP.EmersonProcess.Com
It's
const uint KEY_SCANCODE = 0x0008;
and NOT 0x0004!!!!!!
Oh darn, I only wasted something like 5 hours because of this...
I worked out the code like this, but everytime I send a key with the Press and Release Method...
I get an exception error:
Could not send key: 65
static void SendKey_A()
{
press(65);
release(65);
}
[StructLayout(LayoutKind.Sequential)]
private struct KEYBOARD_INPUT
{
public uint type;
public ushort vk;
public int scanCode;
public uint flags;
public uint time;
public uint extrainfo;
public uint padding1;
public uint padding2;
}
[DllImport("User32.dll")]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] KEYBOARD_INPUT[] input, int structSize);
static void press(int scanCode)
{
sendKey(scanCode, true);
}
static void release(int scanCode)
{
sendKey(scanCode, false);
}
public static void sendKey(int scanCode, bool press)
{
const uint INPUT_KEYBOARD = 1;
const int KEY_EXTENDED = 0x0001;
const uint KEY_UP = 0x0002;
const uint KEY_SCANCODE = 0x0008;
KEYBOARD_INPUT[] input = new KEYBOARD_INPUT[1];
input[0] = new KEYBOARD_INPUT();
input[0].type = INPUT_KEYBOARD;
input[0].flags = KEY_SCANCODE;
if ((scanCode & 0xFF00) == 0xE000)
{ // extended key?
input[0].flags |= KEY_EXTENDED;
}
if (press)
{ // press?
input[0].scanCode = (ushort)(scanCode & 0xFF);
}
else
{ // release?
input[0].scanCode = scanCode;
input[0].flags |= KEY_UP;
}
uint result = SendInput(1, input, Marshal.SizeOf(input[0]));
if (result != 1)
{
throw new Exception("Could not send key: " + scanCode);
}
}
}
}
Post a Comment
<< Home