流媒体机器人模拟按键失效:如何适配各类游戏与应用?
关于SendInput模拟按键兼容性问题的解决方案
核心问题分析
部分游戏或应用会采用DirectInput或自定义输入钩子捕获按键,这类程序往往优先响应硬件级扫描码而非虚拟键;还有些会通过输入上下文(如时间戳、额外信息)判断输入是否来自真实硬件,导致常规模拟失效。
针对性优化方案
- 优先使用扫描码,弱化虚拟键:
你的代码同时传入了wVk和wScan,但很多游戏会忽略虚拟键。建议设置KEYEVENTF_SCANCODE标志,仅填充wScan并将wVk设为0,同时区分按键按下(无额外标志)和释放(追加KEYEVENTF_KEYUP)。 - 填充真实时间戳:
真实硬件输入的time字段会包含系统时间,当前设为0可能被部分应用判定为模拟输入。可以用GetTickCount()获取系统运行时间填充该字段。 - 确保权限与窗口焦点:
- 程序需以管理员权限运行,部分游戏会拒绝普通权限的输入模拟请求。
- 模拟前调用
SetForegroundWindow将目标窗口设为前台,SendInput默认向当前焦点窗口发送输入,若目标窗口未获焦点,输入会直接失效。
- 无需通过PID定向发送:
Windows没有直接通过PID发送输入的API,正确做法是先通过FindWindow/EnumWindows结合PID或窗口标题获取目标窗口句柄,将其设为前台后再发送输入。
优化后的代码片段
use windows::Win32::{ UI::Input::KeyboardAndMouse::{ SendInput, INPUT, INPUT_0, KEYBD_EVENT_FLAGS, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_SCANCODE, KEYEVENTF_KEYUP }, System::Threading::GetTickCount, }; fn simulate_scan_key(scan_code: u16, is_press: bool) { let mut flags = KEYBD_EVENT_FLAGS(KEYEVENTF_SCANCODE.0); if !is_press { flags |= KEYBD_EVENT_FLAGS(KEYEVENTF_KEYUP.0); } let input = INPUT { r#type: INPUT_KEYBOARD, Anonymous: INPUT_0 { ki: KEYBDINPUT { wVk: 0, // 虚拟键设为0,仅使用扫描码 wScan: scan_code, dwFlags: flags, time: unsafe { GetTickCount() }, // 填充系统运行时间 dwExtraInfo: 0, }, }, }; unsafe { SendInput( &[input], std::mem::size_of::<INPUT>().try_into().unwrap(), ); } }
额外注意事项
- 部分反作弊系统(如Easy Anti-Cheat、Battle Eye)会直接拦截SendInput,这类情况无法通过常规方法解决,需查看反作弊开发者文档是否提供白名单机制。
- 测试时覆盖不同类型应用:DirectX游戏、Unity游戏、普通桌面程序,逐一验证兼容性。
内容的提问来源于stack exchange,提问作者Jab
相关产品推荐
相关产品推荐

