Just experimenting with some WinAPI stuff. I have a C# console app that uses SendInput to simulate a keydown and keyup events of some keyboard key. It's not working though, maybe I'm doing something stupid, the key isn't being sent, not sure what am I missing - (trying to send a "W", sleep for 5 secs, open up some text editor to see the letter magically there):
static void Main(string[] args)
{
INPUT input = new INPUT();
input.type = (int)InputType.INPUT_KEYBOARD;
input.U.ki.wScan = 0;
input.U.ki.time = 0;
input.U.ki.dwExtraInfo = UIntPtr.Zero;
input.U.ki.wVk = VirtualKeyShort.KEY_W;
System.Threading.Thread.Sleep(5000);
input.U.ki.dwFlags = KEYEVENTF.KEYDOWN;
NativeCalls.SendInput(1, new INPUT[] { input }, INPUT.Size);
input.U.ki.dwFlags = KEYEVENTF.KEYUP;
NativeCalls.SendInput(1, new INPUT[] { input }, INPUT.Size);
return;
}
Definitions:
public static class NativeCalls
{
[DllImport("user32.dll")]
public static extern uint SendInput(
uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs, int cbSize);
}
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
internal uint type;
internal InputUnion U;
internal static int Size
{
get { return Marshal.SizeOf(typeof(INPUT)); }
}
}
[StructLayout(LayoutKind.Explicit)]
internal struct InputUnion
{
[FieldOffset(0)]
internal KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
internal VirtualKeyShort wVk;
internal ScanCodeShort wScan;
internal KEYEVENTF dwFlags;
internal int time;
internal UIntPtr dwExtraInfo;
}
[Flags]
internal enum KEYEVENTF : uint
{
KEYDOWN = 0x0000,
EXTENDEDKEY = 0x0001,
KEYUP = 0x0002,
SCANCODE = 0x0008,
UNICODE = 0x0004
}
internal enum InputType { INPUT_MOUSE = 0, INPUT_KEYBOARD = 1, INPUT_HARDWARE = 2 }
internal enum VirtualKeyShort : short
{
// keys
KEY_W = 0x57,
// more keys
}
What am I doing wrong? why is the key not being sent?
Thanks :)
Your union declaration is incomplete. Because of that,
Marshal.SizeOf(typeof(INPUT))returns an incorrect value. You do at least need to define theMOUSEINPUTpart of theINPUTunion since that has the largest size. Thepinvoke.netdeclaration should suffice.I expect that
SendInputwould have told you of the problem had you checked the return value. I expect thatSendInputreturnsfalse, and then a call toGetLastErrorshould yieldERROR_INVALID_PARAMETER. Of course, you should not callGetLastErrordirectly. UseSetLastError = true, andMarshal.GetLastWin32Error(). You simply have to perform accurate error checking when you call Win32 API functions.This part of the declaration is wrong at pinvoke.net where they omit
SetLastError.You are using
SendInputin a clumsy way. TheSendInputfunction exists so that you can place multiple input messages in the queue without having them interleaved by others. The documentation makes this clear when it discussesSendInputin relation to the now deprecatedkeybd_eventandmouse_eventfunctions. CallSendInputonce passing an array of length 2.