using anjianjingling.Native; using System.Runtime.InteropServices; namespace anjianjingling.Input { public enum MouseButton { Left, Right, Middle } /// /// 系统级鼠标模拟(SendInput)。 /// public sealed class MouseInput { private static readonly Random Rng = new(); /// 虚拟桌面(含多显示器)尺寸与原点。 private static Rectangle VirtualScreen() { int x = Win32.GetSystemMetrics(Win32.SM_XVIRTUALSCREEN); int y = Win32.GetSystemMetrics(Win32.SM_YVIRTUALSCREEN); int w = Win32.GetSystemMetrics(Win32.SM_CXVIRTUALSCREEN); int h = Win32.GetSystemMetrics(Win32.SM_CYVIRTUALSCREEN); return new Rectangle(x, y, w, h); } /// 把屏幕物理坐标换算为 SendInput 绝对坐标(0..65535,含虚拟桌面偏移)。 private static void ToAbsolute(int x, int y, out int ax, out int ay) { var vs = VirtualScreen(); ax = (int)((x - vs.Left) * 65535.0 / (vs.Width - 1)); ay = (int)((y - vs.Top) * 65535.0 / (vs.Height - 1)); } private static void SendSingle(Win32.MOUSEINPUT mi) { var input = new Win32.INPUT { type = Win32.INPUT_MOUSE, u = new Win32.INPUTUNION { mi = mi } }; Win32.SendInput(1, new[] { input }, Marshal.SizeOf()); } /// 移动鼠标到指定屏幕坐标(瞬时)。 public void MoveTo(int x, int y) { ToAbsolute(x, y, out int ax, out int ay); SendSingle(new Win32.MOUSEINPUT { dx = ax, dy = ay, dwFlags = Win32.MOUSEEVENTF_MOVE | Win32.MOUSEEVENTF_ABSOLUTE | Win32.MOUSEEVENTF_VIRTUALDESK }); } /// 相对移动。 public void MoveR(int dx, int dy) { var p = Position; MoveTo(p.X + dx, p.Y + dy); } /// 带随机偏移的移动(拟人化)。 public void MoveToJitter(int x, int y, int jitterPx = 3) { MoveTo(x + Rng.Next(-jitterPx, jitterPx + 1), y + Rng.Next(-jitterPx, jitterPx + 1)); } public void ButtonDown(MouseButton button) => SendSingle(new Win32.MOUSEINPUT { dwFlags = button switch { MouseButton.Left => Win32.MOUSEEVENTF_LEFTDOWN, MouseButton.Right => Win32.MOUSEEVENTF_RIGHTDOWN, MouseButton.Middle => Win32.MOUSEEVENTF_MIDDLEDOWN, _ => throw new ArgumentOutOfRangeException(nameof(button)) } }); public void ButtonUp(MouseButton button) => SendSingle(new Win32.MOUSEINPUT { dwFlags = button switch { MouseButton.Left => Win32.MOUSEEVENTF_LEFTUP, MouseButton.Right => Win32.MOUSEEVENTF_RIGHTUP, MouseButton.Middle => Win32.MOUSEEVENTF_MIDDLEUP, _ => throw new ArgumentOutOfRangeException(nameof(button)) } }); /// 在当前位置点击;jitter=true 时落点带 ±3px 随机偏移(仅当指定坐标时)。 public void Click(MouseButton button, int? x = null, int? y = null, bool jitter = false) { if (x.HasValue && y.HasValue) { if (jitter) MoveToJitter(x.Value, y.Value); else MoveTo(x.Value, y.Value); Thread.Sleep(60 + Rng.Next(0, 50)); } ButtonDown(button); Thread.Sleep(40 + Rng.Next(0, 40)); ButtonUp(button); } /// 双击(左键)。 public void DoubleClick(int? x = null, int? y = null, bool jitter = false) { Click(MouseButton.Left, x, y, jitter); Thread.Sleep(50 + Rng.Next(0, 60)); Click(MouseButton.Left); } /// 滚轮:正值向上滚,负值向下滚,单位为格(每格 120)。 public void Wheel(int delta) { SendSingle(new Win32.MOUSEINPUT { mouseData = (uint)(delta * Win32.WHEEL_DELTA), dwFlags = Win32.MOUSEEVENTF_WHEEL }); } /// 从一点拖拽到另一点,按 duration 插值移动,模拟人手。 public void Drag(int x1, int y1, int x2, int y2, int durationMs = 400) { MoveTo(x1, y1); Thread.Sleep(100); ButtonDown(MouseButton.Left); int steps = Math.Max(5, durationMs / 16); for (int i = 1; i <= steps; i++) { double t = (double)i / steps; // 轻微非线性,模拟加减速 double e = t * t * (3 - 2 * t); int cx = (int)(x1 + (x2 - x1) * e); int cy = (int)(y1 + (y2 - y1) * e); MoveTo(cx, cy); Thread.Sleep(16); } Thread.Sleep(80); ButtonUp(MouseButton.Left); } /// 当前鼠标屏幕坐标。 public Point Position { get { Win32.GetCursorPos(out var p); return p; } } } }