private static void SendMouseInput(int x, int y, int data, SendMouseInputFlags flags) { PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); permissions.Demand(); uint intflags = (uint)flags; if ((intflags & (int)SendMouseInputFlags.Absolute) != 0) { // Absolute position requires normalized coordinates. NormalizeCoordinates(ref x, ref y); intflags |= NativeMethods.MouseeventfVirtualdesk; } INPUT mouseInput = new INPUT(); mouseInput.Type = NativeMethods.INPUT_MOUSE; mouseInput.Data.Mouse.dx = x; mouseInput.Data.Mouse.dy = y; mouseInput.Data.Mouse.mouseData = data; mouseInput.Data.Mouse.dwFlags = intflags; mouseInput.Data.Mouse.time = 0; mouseInput.Data.Mouse.dwExtraInfo = new IntPtr(0); if (NativeMethods.SendInput(1, new INPUT[] { mouseInput }, Marshal.SizeOf(mouseInput)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } }
/// <summary> /// Performs a mouse-up operation for a specified mouse button. /// </summary> /// <param name="mouseButton">The mouse button to use.</param> public static void Up(MouseButton mouseButton) { int additionalData; SendMouseInputFlags inputFlags = GetInputFlags(mouseButton, true, out additionalData); SendMouseInput(0, 0, additionalData, inputFlags); }
private static void SendMouseInput(int x, int y, int data, SendMouseInputFlags flags) { var permissions = new PermissionSet(PermissionState.Unrestricted); permissions.Demand(); int dX = x, dY = y; if (((int)flags & (int)SendMouseInputFlags.Absolute) != 0) { int vScreenWidth = GetSystemMetrics(78); // SMCxvirtualscreen int vScreenHeight = GetSystemMetrics(79); // SMCyvirtualscreen int vScreenLeft = GetSystemMetrics(76); // SMXvirtualscreen int vScreenTop = GetSystemMetrics(77); // SMYvirtualscreen dX = (x - vScreenLeft) * 65536 / vScreenWidth + 65536 / (vScreenWidth * 2); dY = (y - vScreenTop) * 65536 / vScreenHeight + 65536 / (vScreenHeight * 2); } var input = new INPUT() { Type = 0, MouseInput = new MOUSEINPUT() { dX = dX, dY = dY, mouseData = data, dwFlags = (int)flags, time = 0, dwExtraInfo = new IntPtr(0) } }; SendInput(1, ref input, Marshal.SizeOf(input)); }
/// <summary> /// Inject pointer input into the system /// </summary> /// <param name="x">x coordinate of pointer, if Move flag specified</param> /// <param name="y">y coordinate of pointer, if Move flag specified</param> /// <param name="data">wheel movement, or mouse X button, depending on flags</param> /// <param name="flags">flags to indicate which type of input occurred - move, button press/release, wheel move, etc.</param> /// <remarks>x, y are in pixels. If Absolute flag used, are relative to desktop origin.</remarks> /// <outside_see conditional="false"> /// This API does not work inside the secure execution environment. /// <exception cref="System.Security.Permissions.SecurityPermission"/> /// </outside_see> private static void SendMouseInput(double x, double y, int data, SendMouseInputFlags flags) { //CASRemoval:AutomationPermission.Demand( AutomationPermissionFlag.Input ); int intflags = (int)flags; if ((intflags & (int)SendMouseInputFlags.Absolute) != 0) { int vscreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); int vscreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); int vscreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN); int vscreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN); // Absolute input requires that input is in 'normalized' coords - with the entire // desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this // first. // // In this normalized world, any pixel on the screen corresponds to a block of values // of normalized coords - eg. on a 1024x768 screen, // y pixel 0 corresponds to range 0 to 85.333, // y pixel 1 corresponds to range 85.333 to 170.666, // y pixel 2 correpsonds to range 170.666 to 256 - and so on. // Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range. // However, because int math is used, this can end up being rounded into the wrong // pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as // 85 as an int, which falls into pixel 0's range - and that's where the pointer goes. // To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to // push us into the middle of any given pixel's range - that's the 65536/(Width*2) // part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably // in the middle of that pixel's block. // The key ting here is that unlike points in coordinate geometry, pixels take up // space, so are often better treated like rectangles - and if you want to target // a particular pixel, target its rectangle's midpoint, not its edge. x = ((x - vscreenLeft) * 65536) / vscreenWidth + 65536 / (vscreenWidth * 2); y = ((y - vscreenTop) * 65536) / vscreenHeight + 65536 / (vscreenHeight * 2); intflags |= MOUSEEVENTF_VIRTUALDESK; } INPUT mi = new INPUT(); mi.type = Const.INPUT_MOUSE; mi.union.mouseInput.dx = (int)x; mi.union.mouseInput.dy = (int)y; mi.union.mouseInput.mouseData = data; mi.union.mouseInput.dwFlags = intflags; mi.union.mouseInput.time = 0; mi.union.mouseInput.dwExtraInfo = new IntPtr(0); //Console.WriteLine("Sending"); if (SendInput(1, ref mi, Marshal.SizeOf(mi)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } }
/// <summary> /// Does a mouse down at the center of the FrameworkElement with the specified button. /// </summary> /// <param name="elem">The FrameworkElement.</param> /// <param name="strButton">The FrameworkElement value of the mouse button to use.</param> public static void MouseButtonCenter(FrameworkElement elem, string strButton) { UIElement uie = elem as UIElement; System.Drawing.Rectangle rc = Microsoft.Test.RenderingVerification.ImageUtility.GetScreenBoundingRectangle(uie); SendMouseInputFlags pointerInputFlags = (SendMouseInputFlags)Enum.Parse(typeof(SendMouseInputFlags), strButton); Input.SendMouseInput((rc.Left + rc.Right) / 2, (rc.Top + rc.Bottom) / 2, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute); Input.SendMouseInput((rc.Left + rc.Right) / 2, (rc.Top + rc.Bottom) / 2, 0, pointerInputFlags | SendMouseInputFlags.Absolute); }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods // Inject pointer input into the system // x, y are in pixels. If Absolute flag used, are relative to desktop origin. internal static void SendMouseInput( double x, double y, int data, SendMouseInputFlags flags ) { int intflags = (int) flags; if( ( intflags & (int)SendMouseInputFlags.Absolute ) != 0 ) { int vscreenWidth = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXVIRTUALSCREEN); int vscreenHeight = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYVIRTUALSCREEN); int vscreenLeft = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_XVIRTUALSCREEN); int vscreenTop = UnsafeNativeMethods.GetSystemMetrics( NativeMethods.SM_YVIRTUALSCREEN ); // Absolute input requires that input is in 'normalized' coords - with the entire // desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this // first. // // In this normalized world, any pixel on the screen corresponds to a block of values // of normalized coords - eg. on a 1024x768 screen, // y pixel 0 corresponds to range 0 to 85.333, // y pixel 1 corresponds to range 85.333 to 170.666, // y pixel 2 correpsonds to range 170.666 to 256 - and so on. // Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range. // However, because int math is used, this can end up being rounded into the wrong // pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as // 85 as an int, which falls into pixel 0's range - and that's where the pointer goes. // To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to // push us into the middle of any given pixel's range - that's the 65536/(Width*2) // part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably // in the middle of that pixel's block. // The key ting here is that unlike points in coordinate geometry, pixels take up // space, so are often better treated like rectangles - and if you want to target // a particular pixel, target its rectangle's midpoint, not its edge. x = ( ( x - vscreenLeft ) * 65536 ) / vscreenWidth + 65536 / ( vscreenWidth * 2 ); y = ( ( y - vscreenTop ) * 65536 ) / vscreenHeight + 65536 / ( vscreenHeight * 2 ); intflags |= NativeMethods.MOUSEEVENTF_VIRTUALDESK; } NativeMethods.INPUT mi = new NativeMethods.INPUT(); mi.type = NativeMethods.INPUT_MOUSE; mi.union.mouseInput.dx = (int) x; mi.union.mouseInput.dy = (int)y; mi.union.mouseInput.mouseData = data; mi.union.mouseInput.dwFlags = intflags; mi.union.mouseInput.time = 0; mi.union.mouseInput.dwExtraInfo = new IntPtr( 0 ); if( UnsafeNativeMethods.SendInput( 1, ref mi, Marshal.SizeOf(mi) ) == 0 ) throw new Win32Exception(Marshal.GetLastWin32Error()); }
/// <summary> /// Does a mouse down at x,y on the FrameworkElement with the specified button. /// </summary> /// <param name="elem">The FrameworkElement that x,y values are offset from.</param> /// <param name="x">X position in logical pixels.</param> /// <param name="y">Y position in logical pixels.</param> /// <param name="strButton">The FrameworkElement value of the mouse button to use.</param> public static void MouseButton(FrameworkElement elem, int x, int y, string strButton) { UIElement uie = elem as UIElement; System.Drawing.Rectangle rc = Microsoft.Test.RenderingVerification.ImageUtility.GetScreenBoundingRectangle(uie); SendMouseInputFlags pointerInputFlags = (SendMouseInputFlags)Enum.Parse(typeof(SendMouseInputFlags), strButton); // Convert logical pixel offset to screen pixels x = (int)Monitor.ConvertLogicalToScreen(Dimension.Width, x); y = (int)Monitor.ConvertLogicalToScreen(Dimension.Height, y); Input.SendMouseInput(rc.Left + x, rc.Top + y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute); Input.SendMouseInput(rc.Left + x, rc.Top + y, 0, pointerInputFlags | SendMouseInputFlags.Absolute); }
/// <summary> /// Sends mouse input. /// </summary> /// <param name="x">x coordinate</param> /// <param name="y">y coordinate</param> /// <param name="data">scroll wheel amount</param> /// <param name="flags">SendMouseInputFlags flags</param> private static void SendMouseInput(int x, int y, int data, SendMouseInputFlags flags) { uint intflags = (uint)flags; if ((intflags & (int)SendMouseInputFlags.Absolute) != 0) { // Absolute position requires normalized coordinates. NormalizeCoordinates(ref x, ref y); intflags |= NativeMethods.MouseeventfVirtualdesk; } // don't coalesce mouse moves - tests expect to see the results immediately if ((intflags & (int)SendMouseInputFlags.Move) != 0) { intflags |= NativeMethods.MOUSEEVENTF_MOVE_NOCOALESCE; } var mi = new NativeMethods.INPUT(); mi.Type = NativeMethods.INPUT_MOUSE; mi.Data.Mouse.dx = x; mi.Data.Mouse.dy = y; mi.Data.Mouse.mouseData = data; mi.Data.Mouse.dwFlags = intflags; mi.Data.Mouse.time = 0; mi.Data.Mouse.dwExtraInfo = new IntPtr(0); if (NativeMethods.SendInput(1, new NativeMethods.INPUT[] { mi }, Marshal.SizeOf(mi)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } if ((intflags & (int)SendMouseInputFlags.Wheel) != 0) { // MouseWheel input seems to be getting coalesced by the OS, similar to mouse-move. // There isn't a NOCOALESCE flag to turn this off, so instead just sleep for // a short time, hopefully enough to avoid the coalescing. System.Threading.Thread.Sleep(50); } }
/// <summary> /// Inject pointer input into the system /// </summary> /// <param name="x">x coordinate of pointer, if Move flag specified</param> /// <param name="y">y coordinate of pointer, if Move flag specified</param> /// <param name="data">wheel movement, or mouse X button, depending on flags</param> /// <param name="flags">flags to indicate which type of input occurred - move, button press/release, wheel move, etc.</param> /// <remarks>x, y are in pixels. If Absolute flag used, are relative to desktop origin.</remarks> /// /// <outside_see conditional="false"> /// This API does not work inside the secure execution environment. /// <exception cref="System.Security.Permissions.SecurityPermission"/> /// </outside_see> internal static void SendMouseInputInternal(double x, double y, int data, SendMouseInputFlags flags) { //CASRemoval:AutomationPermission.Demand( AutomationPermissionFlag.Input ); int intflags = (int)flags; switch (MouseMoveMethod) { case MouseMoveMethod.WinForms: // this is the code used in .NET 4.0(?)--4.8 timeframe case MouseMoveMethod.SetCursorPos: // like above, except calls SetCursorPos directly if ((intflags & (int)SendMouseInputFlags.Absolute) != 0) { switch (MouseMoveMethod) { case MouseMoveMethod.WinForms: // this method of moving the cursor appears to be more reliable than the previous code, // at least in the case on Vista where the DPI was changed but we haven't rebooted yet. UseWinFormsToMoveMouse(x, y); break; case MouseMoveMethod.SetCursorPos: // WinForms Cursor.set_Position just wraps SetCursorPos. // Why not call it directly. SetCursorPos((int)(x + 0.5), (int)(y + 0.5)); break; } //now clear the absolute flag intflags &= ~(int)SendMouseInputFlags.Absolute; //and set our x and y to zero so we don't move x = y = 0; intflags |= UnsafeNativeMethods.MOUSEEVENTF_VIRTUALDESK; } break; case MouseMoveMethod.SendInput: case MouseMoveMethod.SendInputImpliedMove: // (Microsoft 4/30/2019) I'm seeing problems where tests don't see // the mouse moves they asked for, because the OS coalesces them. // This is fixed by using the no-coalesce flag in SendInput. That // flag is not supported on Vista/2000, but we don't support those // any more (which also renders moot the DPI-change remark above). // normalize coordinates in absolute moves if ((intflags & (int)SendMouseInputFlags.Absolute) != 0) { intflags |= UnsafeNativeMethods.MOUSEEVENTF_VIRTUALDESK; NormalizeAbsoluteCoordinates(ref x, ref y); if (MouseMoveMethod == MouseMoveMethod.SendInputImpliedMove) { // the old code moved the mouse whenever Absolute was // set, even if Move was not. That's a intflags |= (int)SendMouseInputFlags.Move; } } // don't coalesce mouse moves - tests expect to see the results immediately if ((intflags & (int)SendMouseInputFlags.Move) != 0) { intflags |= UnsafeNativeMethods.MOUSEEVENTF_MOVE_NOCOALESCE; } break; default: throw new InvalidOperationException("Test bug - unknown MouseMoveMethod"); } UnsafeNativeMethods.INPUT mi = new UnsafeNativeMethods.INPUT(); mi.type = UnsafeNativeMethods.INPUT_MOUSE; mi.union.mouseInput.dx = (int)x; mi.union.mouseInput.dy = (int)y; mi.union.mouseInput.mouseData = data; mi.union.mouseInput.dwFlags = intflags; mi.union.mouseInput.time = 0; mi.union.mouseInput.dwExtraInfo = new IntPtr(0); //Console.WriteLine("Sending"); if (UnsafeNativeMethods.SendInput(1, ref mi, Marshal.SizeOf(mi)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } if ((intflags & (int)SendMouseInputFlags.Wheel) != 0) { // MouseWheel input seems to be getting coalesced by the OS, similar to mouse-move. // There isn't a NOCOALESCE flag to turn this off, so instead just sleep for // a short time, hopefully enough to avoid the coalescing. System.Threading.Thread.Sleep(50); } }
/// <summary> /// Inject pointer input into the system /// </summary> /// <param name="x">x coordinate of pointer, if Move flag specified</param> /// <param name="y">y coordinate of pointer, if Move flag specified</param> /// <param name="data">wheel movement, or mouse X button, depending on flags</param> /// <param name="flags">flags to indicate which type of input occurred - move, button press/release, wheel move, etc.</param> /// <remarks>x, y are in pixels. If Absolute flag used, are relative to desktop origin.</remarks> /// /// <outside_see conditional="false"> /// This API does not work inside the secure execution environment. /// <exception cref="System.Security.Permissions.SecurityPermission"/> /// </outside_see> public static void SendMouseInput(double x, double y, int data, SendMouseInputFlags flags) { SendMouseInputInternal(x, y, data, flags); }
public static void SendMouseInput(int x, int y, int data, SendMouseInputFlags flags) { SendMouseInput((double)x, (double)y, data, flags); }