Search code examples
c#winapisendinput

SendInput can not properly simulate keyboard in C#


I am trying to simulate mouse and keyboard events by using SendInput() in C#. The mouse simulated perfectly, while the simulation for the keyboard is not working.

In that case, I tried to use another language to hook the keyboard to determine if it really works, but I could not hook anything. I tried to see if there are any errors, but Marshal.GetLastWin32Error() returns 0, which means there is no error. I also tried to run as administrator, but it did not work.

After that, I found a similar situation on StackOverflow, and I tried to change the code to KeyEventFlags.KEYDOWN | KeyEventFlags.SCANCODE and KeyEventFlags.KEYDOWN | KeyEventFlags.UNICODE, but they both did not work.

Here is the code:

[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
KEYBDINPUT keyboardInput = new KEYBDINPUT();
keyboardInput.dwFlags = (uint)(KeyEventFlags.KEYDOWN);
keyboardInput.wVk = (ushort)'A';
    
InputUnion union = new InputUnion();
union.ki = keyboardInput;

INPUT[] input = new INPUT[1];
input[0].type = (uint)InputTypes.KEYBOARD;
input[0].U = union;
Console.WriteLine(SendInput(1, input, Marshal.SizeOf(union)));
Console.WriteLine(Marshal.GetLastWin32Error());

Note: I only press the key down, but it should still return 1 and have been hooked, while it returns 0 now.

Here is the code that defines enums:

public enum InputTypes : uint
{
    MOUSE = 0,
    KEYBOARD = 1,
    HARDWARE = 2
}

public enum MouseEventFlags : uint
{
    ABSOLUTE = 0x8000,
    LEFTDOWN = 0x0002,
    LEFTUP = 0x0004,
    MIDDLEDOWN = 0x0020,
    MIDDLEUP = 0x0040,
    MOVE = 0x0001,
    RIGHTDOWN = 0x0008,
    RIGHTUP = 0x0010,
    WHEEL = 0x0800,
    XDOWN = 0x0080,
    XUP = 0x0100,
    HWHEEL = 0x01000
}

public enum KeyEventFlags : ushort
{
    KEYDOWN = 0x0000,
    EXTENDEDKEY = 0x0001,
    KEYUP = 0x0002,
    UNICODE = 0x0004,
    SCANCODE = 0x0008
}

[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
    public uint type;
    public InputUnion U;
}

[StructLayout(LayoutKind.Explicit)]
public struct InputUnion
{
    [FieldOffset(0)]
    public MOUSEINPUT mi;
    [FieldOffset(0)]
    public KEYBDINPUT ki;
    [FieldOffset(0)]
    public HARDWAREINPUT hi;
}

[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
    public int dx;
    public int dy;
    public uint mouseData;
    public uint dwFlags;
    public uint time;
    public IntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
    public ushort wVk;
    public ushort wScan;
    public uint dwFlags;
    public uint time;
    public IntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
    public uint uMsg;
    public ushort wParamL;
    public ushort wParamH;
}

I wonder why this is not working, and which step I got wrong.


Solution

  • As far as I'm concerned, you need to use KEYEVENTF_KEYUP to release the key.

    And I agree with Simon, cbSize is the size, in bytes, of an INPUT structure. You should need to use Marshal.SizeOf(typeof(INPUT)) instead of Marshal.SizeOf(union).

    You could refer to the links:

    Simulate keyboard input in C#

    https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput#example