You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:31:35