SendInput和64位

10
以下是我使用SendInput API模拟按键的一些代码,如果我将应用程序设置为编译x86 CPU,则此代码可以正常工作,但对于x64 CPU编译则无法正常工作。我猜想可能与x64使用双倍大小指针有关,但我尝试将[FieldOffset(4)]更改为[FieldOffset(8)],但没有起作用。这可能与它正在导入user32.dll的32位版本有关吗?
    #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]));
    }

1
某些代码似乎遗漏了,因此涉及的那行应该写成 [StructLayout(LayoutKind.Explicit, Size = 28)] - Cheetah
3个回答

12

除了SLaks指出的错误之外,你剩下的问题是INPUT的大小不正确。这意味着SendInput失败,因为它接收到了一个INPUT[]类型的参数。你不能使用StructLayout(LayoutKind.Explicit, Size = 28)来指定大小,因为你想要处理x86和x64代码。

所有这些都源于你只在INPUT中包含了KEYBRDINPUT结构体。而MOUSEINPUT结构体比KEYBRDINPUT结构体更大,这就是问题的根本原因。

最好的解决方案是正确地定义INPUT结构,包括联合部分。可以像这样进行声明(声明摘自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;
}

我开始这个项目时必须使用SendInput,但我不记得为什么了。然而 - 这很有效!非常感谢你! - Cheetah

5

dwExtraInfo 是一个指针。
因此,在32位代码中,它需要是4个字节宽,在64位代码中则需要8个字节。

在C#中实现这一点,使用 IntPtr(而不是始终为4个字节的 uint


我尝试更改这个,但它再次无法与x64一起工作(x86对更改没有问题)。谢谢。 - Cheetah
1
@Ben:你的尺寸是错误的。尺寸随着位数的不同而变化,因此你不能指定它。 - SLaks
是的,请删除 LayoutKind.Explicit, Size = 28FieldOffset 属性。 - David Heffernan
如果我删除它们,x86和x64都无法工作。我相信它们是必需的。我尝试将28更改为32,并将FieldOffset更改为8,只是为了看看是否可以让x64工作,但它并没有起作用。@David Heffernan - Cheetah
你需要使用LayoutKind.Sequential。默认的LayoutKind是Automatic吗?我不确定。 - David Heffernan
@David Heffernan 如果我设置了 LayoutKind.Auto,那么我该如何处理 static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize) 的第三个参数呢? - Cheetah

0
64位平台唯一需要更改的是INPUT的第二个参数的FieldOffset(8)(而不是4)。无需更改或指定其余结构的大小。这已经足够完整(请注意,我使用默认的InputUnion结构,而不是KEYBDINPUT,两者没有区别):
 [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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接