Search code examples
c#console-applicationxinput

C# SendInput() always returns 0 in Console Application


I have developed a simple Console application to poll an Xbox Controller using xinput. I would like to use the values obtained from one of the thumbsticks to move the mouse. I am able to get the x and y values from the thumbstick, but when I use those values to SendInput() (using the User32.dll), the mouse does not move and the return value is 0.

According to Microsoft, "If the function returns zero, the input was already blocked by another thread."

How do I find the other thread that is blocking it? It is just a simple Console Application (exe) started by Visual Studio that prints the x and y values to the screen and attempts to move the mouse.

long x = controller.x;  // values from the controller
long y = controller.y;  // these are checked and do contain numbers

INPUT mouseMoveInput =  new INPUT();
mouseMoveInput.type = 0; // mouse
mouseMoveInput.mi.dx = x;
mouseMoveInput.mi.dy = y;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;

var result = SendInput(1, ref mouseMoveInput, Marshal.SizeOf(new INPUT());
// result always returns 0

Am I missing something? Should this work?

Here are declarations:

    [StructLayout(LayoutKind.Explicit)]
    public struct MOUSEINPUT
    {
        [FieldOffset(0)]
        public long X;

        [FieldOffset(8)]
        public long Y;

        [FieldOffset(16)]
        public uint MouseData;

        [FieldOffset(20)]
        public uint Flags;

        [FieldOffset(24)]
        public uint Time;

        [FieldOffset(28)]
        public IntPtr ExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct KEYBOARDINPUT
    {
        public ushort Vk;
        public ushort Scan;
        public uint Flags;
        public uint Time;
        public IntPtr ExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct HARDWAREINPUT
    {
        public uint Msg;
        public ushort ParamL;
        public ushort ParamH;
    }


    [StructLayout(LayoutKind.Explicit)]
    public struct INPUT
    {
        [FieldOffset(0)]
        public uint type;

        [FieldOffset(4)]
        public MOUSEINPUT mi;

        [FieldOffset(4)]
        public KEYBOARDINPUT ki;

        [FieldOffset(4)]
        public HARDWAREINPUT hi;
    }

UPDATE: using mouse-event does work, but this function is deprecated. Is there a problem with using it anyway since it works?

There is something odd I'm getting with the struct sizes:

Size of tagINPUT: 40
Size of mouseMoveInput: 40
Size of MOUSEINPUT: 32
Size of uint: 4

But if tagINPUT consists of MOUSEINPUT and uint then shouldn't it's size be 36?


Solution

  • The 2nd parameter of SendInput should be a pointer to an array, not a ref parameter, and especially not a ref directly to the struct.

    I would also use explicit layout only for the struct that actually needs it, and let the rest be sequential. It's easier.

    This code works for me:

    const int INPUT_MOUSE = 0;
    const int MOUSEEVENTF_MOVE = 0x0001;
    
    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] inputs, int sizeOfInputStructure);
    
    void Main()
    {
        INPUT mouseMoveInput = new INPUT();
        mouseMoveInput.type = INPUT_MOUSE;
        mouseMoveInput.mi.dx = 10;
        mouseMoveInput.mi.dy = 10;
        mouseMoveInput.mi.mouseData = 0;
        mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
    
        var result = SendInput(1, new INPUT[] { mouseMoveInput}, Marshal.SizeOf(mouseMoveInput));
        if(result == 0) {
            throw new Win32Exception();
        }
    }
    
    [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 Msg;
        public ushort ParamL;
        public ushort ParamH;
    }
    
    
    [StructLayout(LayoutKind.Explicit)]
    public struct INPUT
    {
        [FieldOffset(0)]
        public uint type;
        [FieldOffset(4)]
        public MOUSEINPUT mi;
        [FieldOffset(4)]
        public KEYBDINPUT ki;
        [FieldOffset(4)]
        public HARDWAREINPUT hi;
    }