10

Below is an extract of some code I am using to simulate key presses via the SendInput API. This works correctly if I set my application to compile for an x86 CPU, but doesn't work for x64 CPU compilation.

I'm guessing it has something todo with the fact that x64 uses double size pointers, but I tried to change this [FieldOffset(4)] to this [FieldOffset(8)] but it didn't work.

Could it be something to do with the fact it is importing the 32bit version of user32.dll?

    #region SendInput API

    [DllImport("user32.dll", EntryPoint = "SendInput", SetLastError = true)]
    static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [DllImport("user32.dll", EntryPoint = "GetMessageExtraInfo", SetLastError = true)]
    static extern IntPtr GetMessageExtraInfo();

    private enum KeyEvent
    {
        KeyUp = 0x0002,
        KeyDown = 0x0000,
        ExtendedKey = 0x0001
    }

    private struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public long time;
        public uint dwExtraInfo;
    };

    [StructLayout(LayoutKind.Explicit, Size = 28)]
    private struct INPUT
    {
        [FieldOffset(0)]
        public uint type;
        [FieldOffset(4)]
        public KEYBDINPUT ki;
    };

    #endregion

    public void sendKey(KeyCode Key)
    {
        INPUT[] InputList = new INPUT[2];

        INPUT keyInput = new INPUT();
        keyInput.type = 1;

        keyInput.ki.wScan = 0;
        keyInput.ki.time = 0;
        keyInput.ki.dwFlags = (int)KeyEvent.KeyDown;
        keyInput.ki.dwExtraInfo = (uint)GetMessageExtraInfo();
        keyInput.ki.wVk = (ushort)Key;

        InputList[0] = keyInput;

        keyInput.ki.dwFlags = (int)KeyEvent.KeyUp;

        InputList[1] = keyInput;

        SendInput((uint)2, InputList, Marshal.SizeOf(InputList[0]));
    }
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Cheetah
  • 13,785
  • 31
  • 106
  • 190
  • 1
    Somehow, some code was missing so the line in question should read `[StructLayout(LayoutKind.Explicit, Size = 28)]` – Cheetah Jul 26 '11 at 15:11

3 Answers3

12

Further to the error that SLaks identified, your remaining problem is that the size of INPUT is incorrect. This means that SendInput fails since it receives a parameter of type INPUT[]. You can't specify the size with StructLayout(LayoutKind.Explicit, Size = 28) since you want code that handles both x86 and x64.

This all stems from the fact that you have only included the the KEYBRDINPUT struct in INPUT. The MOUSEINPUT struct is larger than KEYBRDINPUT which is the cause of your problem.

The best solution is to define the INPUT structure correctly, including the union part. Do this like so (declarations taken from pinvoke.net).

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

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

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

[StructLayout(LayoutKind.Explicit)]
struct MouseKeybdHardwareInputUnion
{
    [FieldOffset(0)]
    public MOUSEINPUT mi;

    [FieldOffset(0)]
    public KEYBDINPUT ki;

    [FieldOffset(0)]
    public HARDWAREINPUT hi;
}

[StructLayout(LayoutKind.Sequential)]
struct INPUT
{
    public uint type;
    public MouseKeybdHardwareInputUnion mkhi;
}
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • There was a reason I had to use SendInput when I started this project but I can't remember why. However - this works! Thank you VERY much! – Cheetah Jul 26 '11 at 19:30
5

dwExtraInfo is a pointer.
Therefore, it needs to be 4 bytes wide in 32-bit code and 8 bytes in 64-bit code.

To do that in C#, use IntPtr (not uint, which is always 4 bytes)

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • I tried changing this but it DIDN'T work with x64 again (x86 was fine with the change). Thanks. – Cheetah Jul 26 '11 at 15:04
  • 1
    @Ben: Your size is wrong. The size varies with bitness, so you can't specify it. – SLaks Jul 26 '11 at 15:11
  • Yes, remove `LayoutKind.Explicit, Size = 28` and the `FieldOffset` attributes. – David Heffernan Jul 26 '11 at 15:15
  • @David Heffernan If I remove those, neither x86 or x64 work. I believe they are needed. I tried changing 28 -> 32 and the FieldOffset to 8 just to see if I could get x64 to work and it doesn't – Cheetah Jul 26 '11 at 15:18
  • You need to use LayoutKind.Sequential presumably. Is the default LayoutKind.Automatic? I'm not sure. – David Heffernan Jul 26 '11 at 15:34
  • @David Heffernan How would I deal with the third parameter of `static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize)` given that I have set `LayoutKind.Auto`. – Cheetah Jul 26 '11 at 16:08
0

The only thing to change for the 64bit platforms is the FieldOffset(8) (instead of 4) of the INPUT's second parameter. There is no need to change or specify sizes for the rest structures. This is enough and complete (note that I use the default InputUnion structure instead of KEYBDINPUT whereas makes no difference):

 [StructLayout(LayoutKind.Explicit)]
    public struct INPUT
    {
    [FieldOffset(0)] public uint Type;
    [FieldOffset(8)] 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 UInt16 Vk;
    public UInt16 Scan;
    public UInt32 Flags;
    public UInt32 Time;
    public IntPtr ExtraInfo;
}

    [StructLayout(LayoutKind.Sequential)]
    public struct HARDWAREINPUT
    {
        public uint uMsg;
        public ushort wParamL;
        public ushort wParamH;
    }
anefeletos
  • 672
  • 7
  • 19