如何在C#中编程实现按键持续按住指定时长?
在C#中模拟按住按键持续一段时间
嗨,这个需求其实挺常见的——要实现的不是单次按键触发,而是按住指定按键一段时间后再松开。.NET框架本身没有直接提供这个功能,不过我们可以借助Windows的原生API来搞定,下面给你两种实用方案:
方案一:使用SendInput API(推荐)
SendInput是Windows官方推荐的输入模拟API,比旧的keybd_event更可靠,能更真实地模拟用户的实际输入操作。
首先我们需要定义Windows API相关的结构体和导入方法,这些是实现的基础:
using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; // 如果你用WinForms,需要引用这个;WPF可以直接用System.Windows.Input.Keys public static class KeyboardSimulator { // 定义Windows API需要的INPUT结构体 [StructLayout(LayoutKind.Sequential)] private struct INPUT { public uint Type; public InputUnion Data; } [StructLayout(LayoutKind.Explicit)] private struct InputUnion { [FieldOffset(0)] public MOUSEINPUT Mouse; [FieldOffset(0)] public KEYBDINPUT Keyboard; [FieldOffset(0)] public HARDWAREINPUT Hardware; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort Vk; public ushort Scan; public uint Flags; public uint Time; public IntPtr ExtraInfo; } // 鼠标和硬件输入的结构体我们用不到,留空即可 [StructLayout(LayoutKind.Sequential)] private struct MOUSEINPUT { } [StructLayout(LayoutKind.Sequential)] private struct HARDWAREINPUT { } // 导入SendInput API [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); // 按键按下/抬起的标记常量 private const uint KEYEVENTF_KEYDOWN = 0x0000; private const uint KEYEVENTF_KEYUP = 0x0002; // 获取字符对应的虚拟键码 [DllImport("user32.dll")] private static extern short VkKeyScan(char ch); /// <summary> /// 同步版:模拟按住指定按键一段时间 /// </summary> /// <param name="keyChar">要按住的字符(比如'A',特殊键可以用(char)Keys.ShiftKey)</param> /// <param name="durationMs">持续时长(毫秒)</param> public static void PressKeyForDuration(char keyChar, int durationMs) { short keyCode = VkKeyScan(keyChar); ushort virtualKey = (ushort)(keyCode & 0xFF); // 构造按键按下的输入事件 INPUT downInput = new INPUT { Type = 1, // 1代表键盘输入类型 Data = new InputUnion { Keyboard = new KEYBDINPUT { Vk = virtualKey, Flags = KEYEVENTF_KEYDOWN } } }; // 构造按键抬起的输入事件 INPUT upInput = new INPUT { Type = 1, Data = new InputUnion { Keyboard = new KEYBDINPUT { Vk = virtualKey, Flags = KEYEVENTF_KEYUP } } }; // 发送按下事件 SendInput(1, new[] { downInput }, Marshal.SizeOf(typeof(INPUT))); // 等待指定时长 Thread.Sleep(durationMs); // 发送抬起事件 SendInput(1, new[] { upInput }, Marshal.SizeOf(typeof(INPUT))); } /// <summary> /// 异步版:适合UI线程,避免界面卡顿 /// </summary> public static async Task PressKeyForDurationAsync(char keyChar, int durationMs) { short keyCode = VkKeyScan(keyChar); ushort virtualKey = (ushort)(keyCode & 0xFF); INPUT downInput = new INPUT { Type = 1, Data = new InputUnion { Keyboard = new KEYBDINPUT { Vk = virtualKey, Flags = KEYEVENTF_KEYDOWN } } }; INPUT upInput = new INPUT { Type = 1, Data = new InputUnion { Keyboard = new KEYBDINPUT { Vk = virtualKey, Flags = KEYEVENTF_KEYUP } } }; SendInput(1, new[] { downInput }, Marshal.SizeOf(typeof(INPUT))); await Task.Delay(durationMs); SendInput(1, new[] { upInput }, Marshal.SizeOf(typeof(INPUT))); } }
使用示例
比如你想按住'A'键1秒,或者按住Shift键2秒:
// 同步调用(适合非UI线程) KeyboardSimulator.PressKeyForDuration('A', 1000); // 异步调用(适合WinForms/WPF的UI线程,防止界面卡住) await KeyboardSimulator.PressKeyForDurationAsync((char)Keys.ShiftKey, 2000);
方案二:使用keybd_event API(兼容性优先)
如果需要兼容比较老的Windows版本,可以用keybd_event——它的用法更简单,但微软已经标记为过时,未来可能会被移除,所以仅作为备选方案:
using System.Runtime.InteropServices; using System.Threading; public static class LegacyKeyboardSimulator { [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo); private const uint KEYEVENTF_KEYDOWN = 0x0000; private const uint KEYEVENTF_KEYUP = 0x0002; public static void PressKeyForDuration(byte virtualKey, int durationMs) { // 按下按键 keybd_event(virtualKey, 0, KEYEVENTF_KEYDOWN, 0); // 等待指定时长 Thread.Sleep(durationMs); // 抬起按键 keybd_event(virtualKey, 0, KEYEVENTF_KEYUP, 0); } }
使用示例
比如按住空格键3秒:
// 空格键的虚拟键码是0x20 LegacyKeyboardSimulator.PressKeyForDuration(0x20, 3000);
一些注意事项
- 虚拟键码:你可以通过
Keys枚举(比如Keys.A、Keys.ControlKey)获取对应键的虚拟键码,或者查Windows官方的虚拟键码表。 - UI线程卡顿:如果在WinForms/WPF的UI线程里用
Thread.Sleep,会导致界面冻结,一定要用异步版本的方法。 - 权限问题:有些程序(比如游戏、管理员权限软件)会阻止输入模拟,这时候需要让你的程序以管理员权限运行。
- 组合键处理:如果要模拟Ctrl+C这类组合键,需要先按下Ctrl,再按下C,等待后先抬起C再抬起Ctrl,逻辑类似但要分步处理。
内容的提问来源于stack exchange,提问作者Emanuel L
相关产品推荐
相关产品推荐

