8

I'm writing a helper for injecting touch in UI-tests using InjectTouchInput.

The injected touch works fine but injecting mouse input after touch does not work.

Mouse.Click(point); // works
Touch.Tap(point); // works
Mouse.Click(point); // does not work, mouse cursor no longer visible on screen.

Calling GetCursorInfo() reveals that cursor is CURSOR_SUPPRESSED and I have not found a way to restore it.

Moving the physical mouse brings back the cursor and clicking things work fine.

How can I restore things so that mouse works again here?

Johan Larsson
  • 17,112
  • 9
  • 74
  • 88
  • Try calling SetCursorPos() after the touch injection – Michael Chourdakis Jan 06 '19 at 21:22
  • I tried different combinations of `SetCursorPos()`, `ShowCursor()` and `SetCursor()` but no dice. [this](https://github.com/GuOrg/Gu.Wpf.UiAutomation/blob/master/Gu.Wpf.UiAutomation.UiTests/Input/TouchTests.cs#L145) is a failing test that reproduces the bug if you feel like trying. – Johan Larsson Jan 06 '19 at 21:23

1 Answers1

1

Use the API SendInput to Simulated mouse input.

PInvoke to SendInput – this is the official way to simulate input. It pushes the input through all of the expected code paths, and is indistinguishable from real input.

Here is the code sample:

public class MouseSimulator
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    struct INPUT
    {
        public SendInputEventType type;
        public MouseKeybdhardwareInputUnion mkhi;
    }
    [StructLayout(LayoutKind.Explicit)]
    struct MouseKeybdhardwareInputUnion
    {
        [FieldOffset(0)]
        public MouseInputData mi;

        [FieldOffset(0)]
        public KEYBDINPUT ki;

        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }
    [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;
    }
    struct MouseInputData
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public MouseEventFlags dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
    [Flags]
    enum MouseEventFlags : 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
    }
    enum SendInputEventType : int
    {
        InputMouse,
        InputKeyboard,
        InputHardware
    }

    public static void MoveMouseButton(int x, int y)
    {
        INPUT mouseMoveInput = new INPUT();
        mouseMoveInput.type = SendInputEventType.InputMouse;
        mouseMoveInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_MOVE| MouseEventFlags.MOUSEEVENTF_ABSOLUTE;
        mouseMoveInput.mkhi.mi.dx = x;  
        mouseMoveInput.mkhi.mi.dy = y;  
        SendInput(1, ref mouseMoveInput, Marshal.SizeOf(new INPUT()));
    }
}
Drake Wu
  • 6,927
  • 1
  • 7
  • 30
  • I'm using `SendInput` for the mouse simulation, should have added that to the question. I'll try your code any way in case I have a bug in my implementation. – Johan Larsson Jan 07 '19 at 10:00
  • Calling `SendInput` with `MOUSEEVENTF_MOVE| MOUSEEVENTF_ABSOLUTE` brings back the cursor so that it is visible on screen but does not update focus and it does not respond to clicks via `SendInput` – Johan Larsson Jan 10 '19 at 14:40