Files
2026-10-24 13:02:19 +08:00

89 lines
3.3 KiB
C#

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、方向键等)");
}
}