如何使用WPF的UI自动化向AutomationElement发送右键单击事件?

5
使用WPF内置的UI自动化,向AutomationElement(如Button)发送左键单击事件很容易(虽然相当冗长):
InvokePattern invokePattern = (InvokePattern) element.GetCurrentPattern(InvokePattern.Pattern);
invokePattern.Invoke();

然而,似乎没有内置的方法将右键单击发送到同一元素。我已经决定使用 P/Invoke 调用 SendInput,但我无法让它起作用。在下面的代码中,当我调用 RightClick() 时,一个上下文菜单弹出,就在光标所在的位置,而不是我期望右键单击的元素位置。因此,它似乎忽略了我提供的坐标,只是使用当前光标位置。

public static void RightClick(this AutomationElement element)
{
    Point p = element.GetClickablePoint();

    NativeStructs.Input input = new NativeStructs.Input
    {
        type = NativeEnums.SendInputEventType.Mouse,
        mouseInput = new NativeStructs.MouseInput
        {
            dx = (int) p.X,
            dy = (int) p.Y,
            mouseData = 0,
            dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightDown,
            time = 0,
            dwExtraInfo = IntPtr.Zero,
        },
    };

    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));

    input.mouseInput.dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightUp;

    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
}

internal static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint SendInput(uint nInputs, ref NativeStructs.Input pInputs, int cbSize);
}

internal static class NativeStructs
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct Input
    {
        public NativeEnums.SendInputEventType type;
        public MouseInput mouseInput;
    }

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

internal static class NativeEnums
{
    internal enum SendInputEventType : int
    {
        Mouse = 0,
        Keyboard = 1,
        Hardware = 2,
    }

    [Flags]
    internal enum MouseEventFlags : uint
    {
        Move = 0x0001,
        LeftDown = 0x0002,
        LeftUp = 0x0004,
        RightDown = 0x0008,
        RightUp = 0x0010,
        MiddleDown = 0x0020,
        MiddleUp = 0x0040,
        XDown = 0x0080,
        XUp = 0x0100,
        Wheel = 0x0800,
        Absolute = 0x8000,
    }
}

请检查这段代码。它可以正常工作。http://kadenautomationtestingplatform.googlecode.com/svn-history/r11/ATP/ATP/ATP.CoreLibs/Common/NativeMethods.cs - user958539
在我的情况下,我使用了automationElement(automationElement.Current.BoundingRectangle)的“BoundingRectangle”属性来获取移动光标所需的x,y坐标。然后,我移动光标,并使用MouseEvent发送右键单击。此外,听起来移动光标是不起作用的;以下内容过去对我有用:[DllImport("user32.dll", EntryPoint = "SetCursorPos")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetCursorPos(int X, int Y); - John Bartels
4个回答

1

MSDN说:

如果指定了MOUSEEVENTF_ABSOLUTE值,则dx和dy包含介于0和65,535之间的标准化绝对坐标。事件过程将这些坐标映射到显示表面上。坐标(0,0)映射到显示表面的左上角;坐标(65535,65535)映射到右下角。在多监视器系统中,坐标映射到主监视器。

这意味着您不能仅使用p.X和p.Y。您需要像以下示例一样进行标准化:

var virtualScreen = System.Windows.Forms.SystemInformation.VirtualScreen;
Int32 x = Convert.ToInt32((p.X - virtualScreen.Left) * 65536 / virtualScreen.Width);
Int32 y = Convert.ToInt32((p.Y - virtualScreen.Top) * 65536 / virtualScreen.Height);

你需要将它与 MOUSEEVENTF_VIRTUALDESK 标志进行 OR 操作。


1

从我的理解来看,您关于使用SendInput模拟UIA元素上的右键单击是正确的。

至于如何在右键单击之前强制光标移动到您的元素上,您可以尝试将MOUSEEVENTF_MOVE标志添加到dwFlags中。

如果仍然无法正常工作,也许可以尝试调用两次SendInput - 一次移动鼠标(使用“dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE”),第二次执行您现在正在进行的右键单击操作。

此外,您是否看过这个项目?

http://inputsimulator.codeplex.com/

目前不确定它的鼠标输入支持有多完整,但它可能会很有用。

此外,这个问题也可能很有用:

在C中使用sendInput与鼠标


0
如果您从一个控件派生,您可以使用以下内容:
var e = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Right);
e.RoutedEvent = Mouse.MouseDownEvent;
this.OnMouseDown(e);

0

我检查了所有答案并编写了最终的工作解决方案。

public static void RightClick(this AutomationElement element)
{
    Point p = element.GetClickablePoint();

    NativeStructs.Input input = new NativeStructs.Input
    {
        type = NativeEnums.SendInputEventType.Mouse,
        mouseInput = new NativeStructs.MouseInput
        {
            dx = 0,
            dy = 0,
            mouseData = 0,
            dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightDown | NativeEnums.MouseEventFlags.Move,
            time = 0,
            dwExtraInfo = IntPtr.Zero,
        },
    };

        var primaryScreen = Screen.PrimaryScreen;
        input.mouseInput.dx = Convert.ToInt32((p.X - primaryScreen.Bounds.Left) * 65536 / primaryScreen.Bounds.Width);
        input.mouseInput.dy = Convert.ToInt32((p.Y - primaryScreen.Bounds.Top) * 65536 / primaryScreen.Bounds.Height);
    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
    input.mouseInput.dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightUp | NativeEnums.MouseEventFlags.Move;
    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
}

internal static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint SendInput(uint nInputs, ref NativeStructs.Input pInputs, int cbSize);
}

internal static class NativeStructs
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct Input
    {
        public NativeEnums.SendInputEventType type;
        public MouseInput mouseInput;
    }

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

internal static class NativeEnums
{
    internal enum SendInputEventType : int
    {
        Mouse = 0,
        Keyboard = 1,
        Hardware = 2,
    }

    [Flags]
    internal enum MouseEventFlags : uint
    {
        Move = 0x0001,
        LeftDown = 0x0002,
        LeftUp = 0x0004,
        RightDown = 0x0008,
        RightUp = 0x0010,
        MiddleDown = 0x0020,
        MiddleUp = 0x0040,
        XDown = 0x0080,
        XUp = 0x0100,
        Wheel = 0x0800,
        Absolute = 0x8000,
    }
}

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