first commit

This commit is contained in:
hero920103 committed 2026-10-24 13:02:19 +08:00
commit 761b95080f
404 files changed
+312608

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
using anjianjingling.Common;
using anjianjingling.Native;
using System.Runtime.InteropServices;
namespace anjianjingling.Input
{
/// <summary>
/// 系统级键盘模拟(SendInput):单键、组合键、Unicode 文本。
/// </summary>
public sealed class KeyboardInput
{
private static void SendKey(Win32.KEYBDINPUT ki)
{
var input = new Win32.INPUT
{
type = Win32.INPUT_KEYBOARD,
u = new Win32.INPUTUNION { ki = ki }
};
Win32.SendInput(1, new[] { input }, Marshal.SizeOf<Win32.INPUT>());
}
/// <summary>发送一次按键(按下并弹起)。</summary>
public void KeyPress(string keyName)
{
ushort vk = ResolveOrThrow(keyName);
KeyDownVk(vk);
Thread.Sleep(30);
KeyUpVk(vk);
}
public void KeyDown(string keyName) => KeyDownVk(ResolveOrThrow(keyName));
public void KeyUp(string keyName) => KeyUpVk(ResolveOrThrow(keyName));
private static void KeyDownVk(ushort vk)
{
ushort scan = (ushort)Win32.MapVirtualKey(vk, Win32.MAPVK_VK_TO_VSC);
uint flags = Win32.KEYEVENTF_SCANCODE;
if (VirtualKeys.IsExtended(vk)) flags |= Win32.KEYEVENTF_EXTENDEDKEY;
SendKey(new Win32.KEYBDINPUT { wVk = vk, wScan = scan, dwFlags = flags });
}
private static void KeyUpVk(ushort vk)
{
ushort scan = (ushort)Win32.MapVirtualKey(vk, Win32.MAPVK_VK_TO_VSC);
uint flags = Win32.KEYEVENTF_SCANCODE | Win32.KEYEVENTF_KEYUP;
if (VirtualKeys.IsExtended(vk)) flags |= Win32.KEYEVENTF_EXTENDEDKEY;
SendKey(new Win32.KEYBDINPUT { wVk = vk, wScan = scan, dwFlags = flags });
}
/// <summary>
/// 组合键,如 "Ctrl+A"、"Ctrl+Shift+Esc":修饰键依次按下,主键按下弹起,修饰键逆序弹起。
/// </summary>
public void HotKey(string expression)
{
var parts = expression.Split('+', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 0) throw new ArgumentException($"无法解析组合键:{expression}");
var vks = parts.Select(ResolveOrThrow).ToArray();
for (int i = 0; i < vks.Length - 1; i++) KeyDownVk(vks[i]);
Thread.Sleep(40);
KeyDownVk(vks[^1]);
Thread.Sleep(40);
KeyUpVk(vks[^1]);
for (int i = vks.Length - 2; i >= 0; i--)
{
Thread.Sleep(20);
KeyUpVk(vks[i]);
}
}
/// <summary>
/// 输入整段文本(含中文),使用 KEYEVENTF_UNICODE,不依赖键盘布局。
/// </summary>
public void TypeText(string text)
{
foreach (char c in text)
{
SendKey(new Win32.KEYBDINPUT { wScan = c, dwFlags = Win32.KEYEVENTF_UNICODE });
SendKey(new Win32.KEYBDINPUT { wScan = c, dwFlags = Win32.KEYEVENTF_UNICODE | Win32.KEYEVENTF_KEYUP });
}
}
private static ushort ResolveOrThrow(string name)
=> VirtualKeys.Resolve(name)
?? throw new ArgumentException($"未知按键名:\"{name}\"(可用如 A、F5、ENTER、SPACE、方向键等)");
}
}
+151
View File
@@ -0,0 +1,151 @@
using anjianjingling.Native;
using System.Runtime.InteropServices;
namespace anjianjingling.Input
{
public enum MouseButton { Left, Right, Middle }
/// <summary>
/// 系统级鼠标模拟(SendInput)。
/// </summary>
public sealed class MouseInput
{
private static readonly Random Rng = new();
/// <summary>虚拟桌面(含多显示器)尺寸与原点。</summary>
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);
}
/// <summary>把屏幕物理坐标换算为 SendInput 绝对坐标(0..65535,含虚拟桌面偏移)。</summary>
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<Win32.INPUT>());
}
/// <summary>移动鼠标到指定屏幕坐标(瞬时)。</summary>
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
});
}
/// <summary>相对移动。</summary>
public void MoveR(int dx, int dy)
{
var p = Position;
MoveTo(p.X + dx, p.Y + dy);
}
/// <summary>带随机偏移的移动(拟人化)。</summary>
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))
} });
/// <summary>在当前位置点击;jitter=true 时落点带 ±3px 随机偏移(仅当指定坐标时)。</summary>
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);
}
/// <summary>双击(左键)。</summary>
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);
}
/// <summary>滚轮:正值向上滚,负值向下滚,单位为格(每格 120)。</summary>
public void Wheel(int delta)
{
SendSingle(new Win32.MOUSEINPUT
{
mouseData = (uint)(delta * Win32.WHEEL_DELTA),
dwFlags = Win32.MOUSEEVENTF_WHEEL
});
}
/// <summary>从一点拖拽到另一点,按 duration 插值移动,模拟人手。</summary>
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);
}
/// <summary>当前鼠标屏幕坐标。</summary>
public Point Position
{
get
{
Win32.GetCursorPos(out var p);
return p;
}
}
}
}