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 the MOUSEINPUT
part of the INPUT
union since that has the largest size. The pinvoke.net
declaration should suffice.
I expect that SendInput
would have told you of the problem had you checked the return value. I expect that SendInput
returns false
, and then a call to GetLastError
should yield ERROR_INVALID_PARAMETER
. Of course, you should not call GetLastError
directly. Use SetLastError = true
, and Marshal.GetLastWin32Error()
. You simply have to perform accurate error checking when you call Win32 API functions.
[DllImport("user32.dll", SetLastError=true)]
...
This part of the declaration is wrong at pinvoke.net where they omit SetLastError
.
You are using SendInput
in a clumsy way. The SendInput
function 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 discusses SendInput
in relation to the now deprecated keybd_event
and mouse_event
functions. Call SendInput
once passing an array of length 2.