如何使用SendInput模拟向上键按下(或其他扩展键)

发布于 2025-01-16 10:28:01 字数 4281 浏览 8 评论 0原文

我正在尝试以编程方式模拟按下键盘上的向上箭头键以进行端到端测试。使用以下代码,GUI 控件不会接收任何消息(WM_KEYDOWN、WM_GETDLGCODE、WM_KEYUP)。我使用 Spy++ 来检测传入的消息。你知道为什么\你能建议另一个解决方案吗?

该控件继承自UserControl 类(WinForms)。另外,没有跨线程异常,因为我使用了 Control.InvokeRequired() 和所有相关的东西。

我尝试了2次。第一个是:

[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);


public static int WM_KEYDOWN = 0x100;
public static int WM_KEYUP = 0x101;
public static int VK_UP = 0x26;

public void SimulateKeystrokeEventMethod()
{
    Thread.Sleep(20000);

    Control control = Control.FromHandle(MyGUICOntrol.Handle);
    if (control.InvokeRequired)
    {
        System.Action safeStimulation = delegate
        {
            SetForegroundWindow(Handle);

            const uint MK_LBUTTON = 0x0001;
            Point ClickPos = new Point(330, 401); //The position on top of the objects
            IntPtr lParam = (IntPtr)((401 << 16) | 330);
            IntPtr result = IntPtr.Zero;

            SendMessage(Handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), lParam);
            SendMessage(Handle, WM_LBUTTONUP, IntPtr.Zero, lParam);


            for (int i = 0; i < 100; i++)
            {
                lParam = (IntPtr)(0);
                PostMessage(Handle, WM_KEYDOWN, new IntPtr(VK_UP), lParam);
                SendMessage(Handle, WM_GETDLGCODE, 0x00, 0x00);
                PostMessage(Handle, WM_KEYUP, new IntPtr(VK_UP), lParam);
            }
        };
        control.Invoke(safeStimulation);
    }

第二次尝试:

public void SimulateKeystrokeEventMethod()
{
    Thread.Sleep(20000);

    Control control = Control.FromHandle(MyGUICOntrol.Handle);
    if (control.InvokeRequired)
    {
        System.Action safeStimulation = delegate
        {
            SetForegroundWindow(Handle);

            const uint MK_LBUTTON = 0x0001;
            Point ClickPos = new Point(330, 401); //The position on top of the objects
            IntPtr lParam = (IntPtr)((401 << 16) | 330);
            IntPtr result = IntPtr.Zero;

            SendMessage(Handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), lParam);
            SendMessage(Handle, WM_LBUTTONUP, IntPtr.Zero, lParam);

            ////for (ushort tryScanCode = 0x79; tryScanCode < 0xFF; tryScanCode++)
            {
                ushort tryScanCode = 0x48;
                Input[] inputs1 = new Input[]
                {
                    new Input
                    {
                        type = (int) InputType.Keyboard,
                        u = new InputUnion
                        {
                            ki = new KeyboardInput
                            {
                                wVk = 0,
                                wScan = 0x48,
                                dwFlags = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode),
                                dwExtraInfo = GetMessageExtraInfo()
                            }
                        }
                    }
                };

                Input[] inputs2 = new Input[]
                {
                    new Input
                    {
                        type = (int)InputType.Keyboard,
                        u = new InputUnion
                        {
                            ki = new KeyboardInput
                            {
                                wVk = 0,
                                wScan = 0x48,
                                dwFlags = (uint)(KeyEventF.KeyUp | KeyEventF.Scancode),
                                dwExtraInfo = GetMessageExtraInfo()
                            }
                        }
                    }
                };

                SetForegroundWindow(Handle);

                SendInput((uint)inputs1.Length, inputs1, Marshal.SizeOf(typeof(Input)));
                SendMessage(Handle, WM_GETDLGCODE, 0x00, 0x00);
                SendInput((uint)inputs2.Length, inputs2, Marshal.SizeOf(typeof(Input)));
            }

        };
        control.Invoke(safeStimulation);
    }
}

这是来自的后续问题 模拟按下键盘上的向上箭头键键盘

非常感谢您的帮助!

I am trying to programmatically simulate the pressing of the UP arrow key on the keyboard for an end-to-end test. With the below code, the GUI control receives no message (WM_KEYDOWN, WM_GETDLGCODE, WM_KEYUP). I used Spy++ to detect the incoming messages. Do you know why \ can you suggest another solution?

The control inherits from the UserControl class (WinForms). Also there is no cross-thread exception because I used Control.InvokeRequired() and all the relevant stuff.

I did 2 tries. The first one is:

[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);


public static int WM_KEYDOWN = 0x100;
public static int WM_KEYUP = 0x101;
public static int VK_UP = 0x26;

public void SimulateKeystrokeEventMethod()
{
    Thread.Sleep(20000);

    Control control = Control.FromHandle(MyGUICOntrol.Handle);
    if (control.InvokeRequired)
    {
        System.Action safeStimulation = delegate
        {
            SetForegroundWindow(Handle);

            const uint MK_LBUTTON = 0x0001;
            Point ClickPos = new Point(330, 401); //The position on top of the objects
            IntPtr lParam = (IntPtr)((401 << 16) | 330);
            IntPtr result = IntPtr.Zero;

            SendMessage(Handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), lParam);
            SendMessage(Handle, WM_LBUTTONUP, IntPtr.Zero, lParam);


            for (int i = 0; i < 100; i++)
            {
                lParam = (IntPtr)(0);
                PostMessage(Handle, WM_KEYDOWN, new IntPtr(VK_UP), lParam);
                SendMessage(Handle, WM_GETDLGCODE, 0x00, 0x00);
                PostMessage(Handle, WM_KEYUP, new IntPtr(VK_UP), lParam);
            }
        };
        control.Invoke(safeStimulation);
    }

The second try:

public void SimulateKeystrokeEventMethod()
{
    Thread.Sleep(20000);

    Control control = Control.FromHandle(MyGUICOntrol.Handle);
    if (control.InvokeRequired)
    {
        System.Action safeStimulation = delegate
        {
            SetForegroundWindow(Handle);

            const uint MK_LBUTTON = 0x0001;
            Point ClickPos = new Point(330, 401); //The position on top of the objects
            IntPtr lParam = (IntPtr)((401 << 16) | 330);
            IntPtr result = IntPtr.Zero;

            SendMessage(Handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), lParam);
            SendMessage(Handle, WM_LBUTTONUP, IntPtr.Zero, lParam);

            ////for (ushort tryScanCode = 0x79; tryScanCode < 0xFF; tryScanCode++)
            {
                ushort tryScanCode = 0x48;
                Input[] inputs1 = new Input[]
                {
                    new Input
                    {
                        type = (int) InputType.Keyboard,
                        u = new InputUnion
                        {
                            ki = new KeyboardInput
                            {
                                wVk = 0,
                                wScan = 0x48,
                                dwFlags = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode),
                                dwExtraInfo = GetMessageExtraInfo()
                            }
                        }
                    }
                };

                Input[] inputs2 = new Input[]
                {
                    new Input
                    {
                        type = (int)InputType.Keyboard,
                        u = new InputUnion
                        {
                            ki = new KeyboardInput
                            {
                                wVk = 0,
                                wScan = 0x48,
                                dwFlags = (uint)(KeyEventF.KeyUp | KeyEventF.Scancode),
                                dwExtraInfo = GetMessageExtraInfo()
                            }
                        }
                    }
                };

                SetForegroundWindow(Handle);

                SendInput((uint)inputs1.Length, inputs1, Marshal.SizeOf(typeof(Input)));
                SendMessage(Handle, WM_GETDLGCODE, 0x00, 0x00);
                SendInput((uint)inputs2.Length, inputs2, Marshal.SizeOf(typeof(Input)));
            }

        };
        control.Invoke(safeStimulation);
    }
}

This is a follow up question from
Simulate the pressing of the UP arrow key on a keyboard

Your help is much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

合约呢 2025-01-23 10:28:01

关于 SendInput(),与您尝试做的事情相关。

  • 此函数接受 INPUT 结构。您需要在一次调用中将所有按键传递给此函数。这包括按键和按键修饰符(Control、Shift、ALT、菜单等)。
  • 输入将发送到当前的输入接收者,因此您需要确保接收按键的窗口是焦点窗口(或其父窗口)窗口或主窗口,即实际处理按键的窗口)
  • 如果目标窗口属于不同的线程,则调用 SetFocus() 可能会失败,因此我们事先附加到该线程。
  • 如果父主窗口最小化,您需要事先将其调出,以确保它将接收焦点,然后接收我们的输入。

在这里,我使用 GetCurrentThreadId()< /a> 并与 GetWindowThreadProcessId(),验证是否调用者线程和目标线程不同。
如果它们不相同,则 AttachThreadInput () 被调用。

如果目标主窗口最小化(请参阅对 IsIconic()),它被恢复,调用SetWindowPlacement() 并带到前台使用 BringWindowToTop()

然后调用 SetFocus() 将焦点移动到目标句柄(假设它可以接收焦点,也就是说 - 该函数无论如何都应该返回成功)。

最后,您收集需要在不同的 INPUT 结构中发送的所有键(每个键修饰符在其自己的结构中),并对 SendInput() 进行一次调用。


例如:

Up 键发送到目标句柄:

IntPtr handle = [The selected handle];
bool result = NativeMethods.SendKeyboardInput(handle, Keys.Up, null);

发送 Control + Shift + Home

var modifiers = new[] { Keys.ShiftKey, Keys.ControlKey};
bool result = NativeMethods.SendKeyboardInput(handle, Keys.Home, modifiers);

这是这样的它有效:

SendInput test

标准 PictureBox 无法聚焦,因此当 时,您会看到按钮闪烁SetFocus() 被调用

用于测试的示例项目 (Google Drive)

NativeMethods

internal class NativeMethods {

    public static bool SendKeyboardInput(IntPtr hWnd, Keys key, Keys[] modifiers = null, int delay = 0)
    {
        if (hWnd != IntPtr.Zero) {
            uint targetThreadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
            uint currentThreadID = GetCurrentThreadId();

            if (targetThreadID != currentThreadID) {
                try {
                    if (!AttachThreadInput(currentThreadID, targetThreadID, true)) return false;
                    var parentWindow = GetAncestor(hWnd, GetAncestorFlags.GA_ROOT);
                    if (IsIconic(parentWindow)) {
                        if (!RestoreWindow(parentWindow)) return false;
                    }

                    if (!BringWindowToTop(parentWindow)) return false;
                    if (IntPtr.Zero == SetFocus(hWnd)) return false;
                }
                finally {
                    AttachThreadInput(currentThreadID, targetThreadID, false);
                }
            }
            else {
                SetFocus(hWnd);
            }
        }

        var flagsKeyDw = IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : KeyboardInputFlags.KeyDown;
        var flagsKeyUp = KeyboardInputFlags.KeyUp | (IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : 0);

        var inputs = new List<INPUT>();
        var input = new INPUT(SendInputType.InputKeyboard);

        // Key Modifiers Down
        if (!(modifiers is null)) {
            foreach (var modifier in modifiers) {
                input.Union.Keyboard.Flags = KeyboardInputFlags.KeyDown;
                input.Union.Keyboard.VirtKeys = (ushort)modifier;
                inputs.Add(input);
            }
        }

        // Key Down
        input.Union.Keyboard.Flags = flagsKeyDw | KeyboardInputFlags.Unicode;
        input.Union.Keyboard.VirtKeys = (ushort)key;
        inputs.Add(input);

        // Key Up
        input.Union.Keyboard.Flags = flagsKeyUp | KeyboardInputFlags.Unicode;
        input.Union.Keyboard.VirtKeys = (ushort)key;
        inputs.Add(input);

        // Key Modifiers Up
        if (!(modifiers is null)) {
            foreach (var modifier in modifiers) {
                input.Union.Keyboard.Flags = KeyboardInputFlags.KeyUp;
                input.Union.Keyboard.VirtKeys = (ushort)modifier;
                inputs.Add(input);
            }
        }

        uint sent = SendInput((uint)inputs.Count(), inputs.ToArray(), Marshal.SizeOf<INPUT>());
        return sent > 0;
    }

    private static Keys[] extendedKeys = { Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Home, Keys.End, Keys.Prior, Keys.Next, Keys.Insert, Keys.Delete };
    private static bool IsExtendedKey(Keys key) => extendedKeys.Contains(key);

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT {
        public SendInputType InputType;
        public InputUnion Union;

        public INPUT(SendInputType type) {
            InputType = type;
            Union = new InputUnion();
        }
    }

    public enum SendInputType : uint {
        InputMouse = 0,
        InputKeyboard = 1,
        InputHardware = 2
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputUnion {
        [FieldOffset(0)]
        public MOUSEINPUT Mouse;

        [FieldOffset(0)]
        public KEYBDINPUT Keyboard;

        [FieldOffset(0)]
        public HARDWAREINPUT Hardware;
    }

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

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT {
        public ushort VirtKeys;
        public ushort wScan;
        public KeyboardInputFlags Flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

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

    [Flags]
    public enum MouseEventdwFlags : uint {
        MOUSEEVENTF_MOVE = 0x0001,
        MOUSEEVENTF_LEFTDOWN = 0x0002,
        MOUSEEVENTF_LEFTUP = 0x0004,
        MOUSEEVENTF_RIGHTDOWN = 0x0008,
        MOUSEEVENTF_RIGHTUP = 0x0010,
        MOUSEEVENTF_MIDDLEDOWN = 0x0020,
        MOUSEEVENTF_MIDDLEUP = 0x0040,
        MOUSEEVENTF_XDOWN = 0x0080,
        MOUSEEVENTF_XUP = 0x0100,
        MOUSEEVENTF_WHEEL = 0x0800,
        MOUSEEVENTF_VIRTUALDESK = 0x4000,
        MOUSEEVENTF_ABSOLUTE = 0x8000
    }

    [Flags]
    public enum KeyboardInputFlags : uint {
        KeyDown = 0x0,
        ExtendedKey = 0x0001,
        KeyUp = 0x0002,
        Scancode = 0x0008,
        Unicode = 0x0004
    }

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowplacement
    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWPLACEMENT {
        public int length;
        public WplFlags flags;
        public SW_Flags showCmd;
        public POINT ptMinPosition;
        public POINT ptMaxPosition;
        public RECT rcNormalPosition;
    }

    public enum WplFlags : uint {
        WPF_ASYNCWINDOWPLACEMENT = 0x0004,   // If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
        WPF_RESTORETOMAXIMIZED = 0x0002,     // The restored window will be maximized, regardless of whether it was maximized before it was minimized. This setting is only valid the next time the window is restored. It does not change the default restoration behavior.
                                             // This flag is only valid when the SW_SHOWMINIMIZED value is specified for the showCmd member.
        WPF_SETMINPOSITION = 0x0001          // The coordinates of the minimized window may be specified. This flag must be specified if the coordinates are set in the ptMinPosition member.
    }

    [Flags]
    public enum SW_Flags : uint {
        SW_HIDE = 0X00,
        SW_SHOWNORMAL = 0x01,
        SW_MAXIMIZE = 0x03,
        SW_SHOWNOACTIVATE = 0x04,
        SW_SHOW = 0x05,
        SW_MINIMIZE = 0x06,
        SW_RESTORE = 0x09,
        SW_SHOWDEFAULT = 0x0A,
        SW_FORCEMINIMIZE = 0x0B
    }

    public enum GetAncestorFlags : uint {
        GA_PARENT = 1,     // Retrieves the parent window.This does not include the owner, as it does with the GetParent function.
        GA_ROOT = 2,       // Retrieves the root window by walking the chain of parent windows.
        GA_ROOTOWNER = 3   // Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent.
    }

    [StructLayout(LayoutKind.Sequential)]
    public class POINT {
        public int x;
        public int y;

        public POINT(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public Point ToPoint() => new Point(this.x, this.y);
        public PointF ToPointF() => new PointF((float)this.x, (float)this.y);
        public POINT FromPoint(Point p) => new POINT(p.X, p.Y);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int left, int top, int right, int bottom) {
            Left = left; Top = top; Right = right; Bottom = bottom;
        }

        public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
        public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right + p.x, Bottom + p.y);

        public RECT FromRectangle(RectangleF rectangle) => FromRectangle(Rectangle.Round(rectangle));
        public RECT FromRectangle(Rectangle rectangle) => new RECT() {
            Left = rectangle.Left, 
            Top = rectangle.Top,
            Bottom = rectangle.Bottom,
            Right = rectangle.Right
        };
        public RECT FromXYWH(int x, int y, int width, int height) => new RECT(x, y, x + width, y + height);
    }


    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowplacement
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool GetWindowPlacement(IntPtr hWnd, [In, Out] ref WINDOWPLACEMENT lpwndpl);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr voidProcessId);

    // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern uint GetCurrentThreadId();

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool AttachThreadInput([In] uint idAttach, [In] uint idAttachTo, [In, MarshalAs(UnmanagedType.Bool)] bool fAttach);

    [ResourceExposure(ResourceScope.None)]
    [DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)]
    internal static extern IntPtr GetAncestor(IntPtr hWnd, GetAncestorFlags flags);

    [DllImport("user32.dll")]
    internal static extern bool IsIconic(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool BringWindowToTop(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr SetFocus(IntPtr hWnd);

    //https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendinput
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint SendInput(uint nInputs, [In, MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs, int cbSize);

    public static bool RestoreWindow(IntPtr hWnd)
    {
        var wpl = new WINDOWPLACEMENT() {
            length = Marshal.SizeOf<WINDOWPLACEMENT>()
        };
        if (!GetWindowPlacement(hWnd, ref wpl)) return false;

        wpl.flags = WplFlags.WPF_ASYNCWINDOWPLACEMENT;
        wpl.showCmd = SW_Flags.SW_RESTORE;
        return SetWindowPlacement(hWnd, ref wpl);
    }
}

A few notes about SendInput(), in relation to what you tried to do.

  • This function accepts and array of INPUT structures. You need to pass all Key presses in a single call to this function. This include a Key and Key modifiers (Control, Shift, ALT, Menu et.)
  • The input is sent to the current Input recipient, so you need to make sure the Window that is meant to receive the key presses is Focused (or its Parent Window or the Main Window, i.e., the window that actually processes the key presses)
  • If the target Window belongs to a different Thread, the call to SetFocus() may fail, so we attach to that Thread beforehand.
  • If the parent Main Window is minimized, you need to bring it up beforehand, to be sure it will receive focus and then our input.

Here, I'm using GetCurrentThreadId() and compare with GetWindowThreadProcessId(), to verify whether the caller Thread and the target Thread are different.
If they're not the same, then AttachThreadInput() is called.

If the target Main Window is minimized (see the call to IsIconic()), it's restored, calling SetWindowPlacement() and brought to the foreground using BringWindowToTop().

Then a call to SetFocus() moves the focus to the target handle (assuming it can receive focus, that is - the function should return success anyway).

In the end, you collect all the Keys you need to send in different INPUT structs (each Key Modifiers in its own struct) and make a single call to SendInput().


For example:

Send the Up key to the target handle:

IntPtr handle = [The selected handle];
bool result = NativeMethods.SendKeyboardInput(handle, Keys.Up, null);

Send Control + Shift + Home:

var modifiers = new[] { Keys.ShiftKey, Keys.ControlKey};
bool result = NativeMethods.SendKeyboardInput(handle, Keys.Home, modifiers);

This is how it works:

SendInput test

A standard PictureBox cannot be focused, so you'll see a Button flicker when SetFocus() is called

Sample Project for testing (Google Drive)

NativeMethods:

internal class NativeMethods {

    public static bool SendKeyboardInput(IntPtr hWnd, Keys key, Keys[] modifiers = null, int delay = 0)
    {
        if (hWnd != IntPtr.Zero) {
            uint targetThreadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
            uint currentThreadID = GetCurrentThreadId();

            if (targetThreadID != currentThreadID) {
                try {
                    if (!AttachThreadInput(currentThreadID, targetThreadID, true)) return false;
                    var parentWindow = GetAncestor(hWnd, GetAncestorFlags.GA_ROOT);
                    if (IsIconic(parentWindow)) {
                        if (!RestoreWindow(parentWindow)) return false;
                    }

                    if (!BringWindowToTop(parentWindow)) return false;
                    if (IntPtr.Zero == SetFocus(hWnd)) return false;
                }
                finally {
                    AttachThreadInput(currentThreadID, targetThreadID, false);
                }
            }
            else {
                SetFocus(hWnd);
            }
        }

        var flagsKeyDw = IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : KeyboardInputFlags.KeyDown;
        var flagsKeyUp = KeyboardInputFlags.KeyUp | (IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : 0);

        var inputs = new List<INPUT>();
        var input = new INPUT(SendInputType.InputKeyboard);

        // Key Modifiers Down
        if (!(modifiers is null)) {
            foreach (var modifier in modifiers) {
                input.Union.Keyboard.Flags = KeyboardInputFlags.KeyDown;
                input.Union.Keyboard.VirtKeys = (ushort)modifier;
                inputs.Add(input);
            }
        }

        // Key Down
        input.Union.Keyboard.Flags = flagsKeyDw | KeyboardInputFlags.Unicode;
        input.Union.Keyboard.VirtKeys = (ushort)key;
        inputs.Add(input);

        // Key Up
        input.Union.Keyboard.Flags = flagsKeyUp | KeyboardInputFlags.Unicode;
        input.Union.Keyboard.VirtKeys = (ushort)key;
        inputs.Add(input);

        // Key Modifiers Up
        if (!(modifiers is null)) {
            foreach (var modifier in modifiers) {
                input.Union.Keyboard.Flags = KeyboardInputFlags.KeyUp;
                input.Union.Keyboard.VirtKeys = (ushort)modifier;
                inputs.Add(input);
            }
        }

        uint sent = SendInput((uint)inputs.Count(), inputs.ToArray(), Marshal.SizeOf<INPUT>());
        return sent > 0;
    }

    private static Keys[] extendedKeys = { Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Home, Keys.End, Keys.Prior, Keys.Next, Keys.Insert, Keys.Delete };
    private static bool IsExtendedKey(Keys key) => extendedKeys.Contains(key);

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT {
        public SendInputType InputType;
        public InputUnion Union;

        public INPUT(SendInputType type) {
            InputType = type;
            Union = new InputUnion();
        }
    }

    public enum SendInputType : uint {
        InputMouse = 0,
        InputKeyboard = 1,
        InputHardware = 2
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputUnion {
        [FieldOffset(0)]
        public MOUSEINPUT Mouse;

        [FieldOffset(0)]
        public KEYBDINPUT Keyboard;

        [FieldOffset(0)]
        public HARDWAREINPUT Hardware;
    }

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

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT {
        public ushort VirtKeys;
        public ushort wScan;
        public KeyboardInputFlags Flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

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

    [Flags]
    public enum MouseEventdwFlags : uint {
        MOUSEEVENTF_MOVE = 0x0001,
        MOUSEEVENTF_LEFTDOWN = 0x0002,
        MOUSEEVENTF_LEFTUP = 0x0004,
        MOUSEEVENTF_RIGHTDOWN = 0x0008,
        MOUSEEVENTF_RIGHTUP = 0x0010,
        MOUSEEVENTF_MIDDLEDOWN = 0x0020,
        MOUSEEVENTF_MIDDLEUP = 0x0040,
        MOUSEEVENTF_XDOWN = 0x0080,
        MOUSEEVENTF_XUP = 0x0100,
        MOUSEEVENTF_WHEEL = 0x0800,
        MOUSEEVENTF_VIRTUALDESK = 0x4000,
        MOUSEEVENTF_ABSOLUTE = 0x8000
    }

    [Flags]
    public enum KeyboardInputFlags : uint {
        KeyDown = 0x0,
        ExtendedKey = 0x0001,
        KeyUp = 0x0002,
        Scancode = 0x0008,
        Unicode = 0x0004
    }

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowplacement
    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWPLACEMENT {
        public int length;
        public WplFlags flags;
        public SW_Flags showCmd;
        public POINT ptMinPosition;
        public POINT ptMaxPosition;
        public RECT rcNormalPosition;
    }

    public enum WplFlags : uint {
        WPF_ASYNCWINDOWPLACEMENT = 0x0004,   // If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
        WPF_RESTORETOMAXIMIZED = 0x0002,     // The restored window will be maximized, regardless of whether it was maximized before it was minimized. This setting is only valid the next time the window is restored. It does not change the default restoration behavior.
                                             // This flag is only valid when the SW_SHOWMINIMIZED value is specified for the showCmd member.
        WPF_SETMINPOSITION = 0x0001          // The coordinates of the minimized window may be specified. This flag must be specified if the coordinates are set in the ptMinPosition member.
    }

    [Flags]
    public enum SW_Flags : uint {
        SW_HIDE = 0X00,
        SW_SHOWNORMAL = 0x01,
        SW_MAXIMIZE = 0x03,
        SW_SHOWNOACTIVATE = 0x04,
        SW_SHOW = 0x05,
        SW_MINIMIZE = 0x06,
        SW_RESTORE = 0x09,
        SW_SHOWDEFAULT = 0x0A,
        SW_FORCEMINIMIZE = 0x0B
    }

    public enum GetAncestorFlags : uint {
        GA_PARENT = 1,     // Retrieves the parent window.This does not include the owner, as it does with the GetParent function.
        GA_ROOT = 2,       // Retrieves the root window by walking the chain of parent windows.
        GA_ROOTOWNER = 3   // Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent.
    }

    [StructLayout(LayoutKind.Sequential)]
    public class POINT {
        public int x;
        public int y;

        public POINT(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public Point ToPoint() => new Point(this.x, this.y);
        public PointF ToPointF() => new PointF((float)this.x, (float)this.y);
        public POINT FromPoint(Point p) => new POINT(p.X, p.Y);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int left, int top, int right, int bottom) {
            Left = left; Top = top; Right = right; Bottom = bottom;
        }

        public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
        public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right + p.x, Bottom + p.y);

        public RECT FromRectangle(RectangleF rectangle) => FromRectangle(Rectangle.Round(rectangle));
        public RECT FromRectangle(Rectangle rectangle) => new RECT() {
            Left = rectangle.Left, 
            Top = rectangle.Top,
            Bottom = rectangle.Bottom,
            Right = rectangle.Right
        };
        public RECT FromXYWH(int x, int y, int width, int height) => new RECT(x, y, x + width, y + height);
    }


    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowplacement
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool GetWindowPlacement(IntPtr hWnd, [In, Out] ref WINDOWPLACEMENT lpwndpl);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr voidProcessId);

    // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern uint GetCurrentThreadId();

    // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool AttachThreadInput([In] uint idAttach, [In] uint idAttachTo, [In, MarshalAs(UnmanagedType.Bool)] bool fAttach);

    [ResourceExposure(ResourceScope.None)]
    [DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)]
    internal static extern IntPtr GetAncestor(IntPtr hWnd, GetAncestorFlags flags);

    [DllImport("user32.dll")]
    internal static extern bool IsIconic(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool BringWindowToTop(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr SetFocus(IntPtr hWnd);

    //https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendinput
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint SendInput(uint nInputs, [In, MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs, int cbSize);

    public static bool RestoreWindow(IntPtr hWnd)
    {
        var wpl = new WINDOWPLACEMENT() {
            length = Marshal.SizeOf<WINDOWPLACEMENT>()
        };
        if (!GetWindowPlacement(hWnd, ref wpl)) return false;

        wpl.flags = WplFlags.WPF_ASYNCWINDOWPLACEMENT;
        wpl.showCmd = SW_Flags.SW_RESTORE;
        return SetWindowPlacement(hWnd, ref wpl);
    }
}
一人独醉 2025-01-23 10:28:01

吉米,你很有帮助!根据你的回答,我修复了我的代码。我针对我的问题提交了另一个答案。

    public void SimulateKeystrokeEventMethod()
    {
        Thread.Sleep(10000);
        IntPtr handle = DesignPlanHelper.Handle;
        ushort key = (ushort)KB_UP;
        int repetitions = 30;

        Control control = Control.FromHandle(handle);

        if (control.InvokeRequired)
        {
            System.Action safeStimulation = delegate
            {
                SetFocus(handle);

                // Select an image lying in the control (a descendant of UserControl in WinForms)
                Clicks(handle, 330, 401);

                Input keyDown = new Input
                {
                        type = (int) InputType.Keyboard,
                        u = new InputUnion
                        {
                            ki = new KeyboardInput
                            {
                                wVk = 0, wScan = key, dwFlags = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode), dwExtraInfo = GetMessageExtraInfo()
                            }
                        }
                };

                Input keyUp = new Input
                {
                    type = (int) InputType.Keyboard,
                    u = new InputUnion
                    {
                        ki = new KeyboardInput
                        {
                            wVk = 0, wScan = key, dwFlags = (uint) (KeyEventF.KeyUp | KeyEventF.Scancode), dwExtraInfo = GetMessageExtraInfo()
                        }
                    }
                };

                Input[] inputs = new Input[repetitions * 2];
                for (int input = 0; input < repetitions*2;)
                {
                    inputs[input] = keyDown;
                    input++;
                    inputs[input] = keyUp;
                    input++;
                }

                // You must pass all Key presses in a single call to this function.
                SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
            };
            control.Invoke(safeStimulation);
        }
        else
        {
            //copy some code from above.
        }
    }

Jimi, you were very helpful! From you answer, I fixed my code. I submit another answer to my problem.

    public void SimulateKeystrokeEventMethod()
    {
        Thread.Sleep(10000);
        IntPtr handle = DesignPlanHelper.Handle;
        ushort key = (ushort)KB_UP;
        int repetitions = 30;

        Control control = Control.FromHandle(handle);

        if (control.InvokeRequired)
        {
            System.Action safeStimulation = delegate
            {
                SetFocus(handle);

                // Select an image lying in the control (a descendant of UserControl in WinForms)
                Clicks(handle, 330, 401);

                Input keyDown = new Input
                {
                        type = (int) InputType.Keyboard,
                        u = new InputUnion
                        {
                            ki = new KeyboardInput
                            {
                                wVk = 0, wScan = key, dwFlags = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode), dwExtraInfo = GetMessageExtraInfo()
                            }
                        }
                };

                Input keyUp = new Input
                {
                    type = (int) InputType.Keyboard,
                    u = new InputUnion
                    {
                        ki = new KeyboardInput
                        {
                            wVk = 0, wScan = key, dwFlags = (uint) (KeyEventF.KeyUp | KeyEventF.Scancode), dwExtraInfo = GetMessageExtraInfo()
                        }
                    }
                };

                Input[] inputs = new Input[repetitions * 2];
                for (int input = 0; input < repetitions*2;)
                {
                    inputs[input] = keyDown;
                    input++;
                    inputs[input] = keyUp;
                    input++;
                }

                // You must pass all Key presses in a single call to this function.
                SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
            };
            control.Invoke(safeStimulation);
        }
        else
        {
            //copy some code from above.
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文