开发者

How can I send a right-click event to an AutomationElement using WPF's UI automation?

Using WPF's built-in UI automation, it's easy (though pretty verbose) to send a left-click event to an AutomationElement (such as a Button):

InvokePattern invokePattern = (InvokePattern) element.GetCurrentPattern(InvokePattern.Pattern);
invokePattern.Invoke();

However, there seems to be no built-in way to send right-clicks to the same element. I've resigned myself to using P/Invoke to call SendInput, but I can't get it to work. With the code below, when I call RightClick(), a context menu pops up right where the cursor is, rather than at the element I expect to be right-clicked. So it seems like it's ignoring the coordinates I hand in and just using the current cursor location.

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.Se开发者_如何学JAVAquential)]
    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,
    }
}


From what I understand, you're correct about SendInput being necessary to simulate a right click on a UIA element.

As for how to force the cursor to move to your element before the right click, you might try adding the MOUSEEVENTF_MOVE flag to your dwFlags.

If that still doesn't work, perhaps try calling SendInput twice - once to move the mouse (with "dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE") and a second time to perform the right click the way you're doing it now.

Also, have you seen this project?

http://inputsimulator.codeplex.com/

Not sure how complete its mouse input support is at the moment, but it might be useful.

Also, this question might be useful too:

using mouse with sendInput in C


MSDN says:

If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. The event procedure maps these coordinates onto the display surface. Coordinate (0,0) maps onto the upper-left corner of the display surface; coordinate (65535,65535) maps onto the lower-right corner. In a multimonitor system, the coordinates map to the primary monitor.

Which means you cannot just use p.X and p.Y. You need to normalize it like this for example:

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);

and you will need to OR it with MOUSEEVENTF_VIRTUALDESK flag too.


If you derive from a control, you can use the following:

var e = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Right);
e.RoutedEvent = Mouse.MouseDownEvent;
this.OnMouseDown(e);


I examined all answeres and compiled final working solution.

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,
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜