基于user32.dll的键鼠宏无法在《使命召唤4:现代战争》中生效
我开发了一款热键触发的后台键鼠录放程序,在Windows系统、Chrome浏览器、《守望先锋》《火箭联盟》等场景下均能正常工作,但在《使命召唤:现代战争(2007)》中,仅鼠标移动位置捕获(GetCursorPos有效),键鼠输入的捕获与回放全部失效。
已尝试的无效实现方式
输入回放类
基于user32.dll的两种键盘模拟方式,在其他场景生效,但该游戏内无响应:
keybd_event((byte)Key, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero); // 或 SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
鼠标移动模拟同样无效:
mouse_event(MOUSEEVENTF_MOVE, movementX, movementY, 0, UIntPtr.Zero);
输入捕获类
GetAsyncKeyState始终返回0:
[DllImport("user32.dll")] static extern short GetAsyncKeyState(Keys vKey); // 调用逻辑 GetAsyncKeyState(key)
- 全局低级键盘钩子(
WH_KEYBOARD_LL)完全无响应:
private static ListBox lb1; public Form1() { InitializeComponent(); lb1 = listBox1; InstallHook(); } private const int WH_KEYBOARD_LL = 13; public delegate int KeyboardHookProc(int code, int wParam, ref KeyboardHookStruct lParam); public struct KeyboardHookStruct { public int vkCode; public int scanCode; public int flags; public int time; public int dwExtraInfo; } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", SetLastError = true)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", SetLastError = true)] private static extern int CallNextHookEx(IntPtr hhk, int nCode, int wParam, ref KeyboardHookStruct lParam); private static IntPtr hookHandle = IntPtr.Zero; private static KeyboardHookProc hookProc = hookProcFunc; public static void InstallHook() { hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, IntPtr.Zero, 0); } public static void UninstallHook() { UnhookWindowsHookEx(hookHandle); } private static int hookProcFunc(int code, int wParam, ref KeyboardHookStruct lParam) { if (code >= 0) { lb1.Items.Add("Keystroke: " + lParam.vkCode); } return CallNextHookEx(hookHandle, code, wParam, ref lParam); }
关键参考信息
AHK(AutoHotkey)和罗技HUB宏可在该游戏中正常工作。
核心疑问
- 问题是否出在我的输入捕获/触发方法上?
- 能否用
user32.dll实现通用键鼠宏? - 若不行,有哪些更通用的替代方案(支持非C#语言如C)?
问题根源
《使命召唤:现代战争(2007)》这类老DirectX游戏,未通过Windows消息队列处理输入,而是直接读取硬件输入端口或使用DirectInput API绕过了user32层的输入模拟/捕获逻辑。你使用的user32.dll接口均基于Windows消息机制,自然会被游戏忽略。
关于user32.dll实现通用宏的可能性
user32.dll的接口(SendInput、各类钩子等)仅能覆盖基于Windows消息的输入场景,对直接读取硬件的游戏无能为力,无法实现真正意义上的通用键鼠宏。
替代方案
1. 硬件级输入模拟(推荐)
这类方案直接模拟键鼠的USB/HID信号,游戏无法区分真实输入与模拟输入,AHK和罗技宏本质就是采用了类似逻辑。
- C/C++实现:使用
libusb或Windows原生HIDClass驱动API,构造HID报告直接发送到系统。 - C#实现:借助
SharpHID或HIDSharp库,封装HID设备的模拟逻辑。 - 注意:需以管理员权限运行,部分系统可能需要禁用驱动签名验证。
2. 游戏进程注入(针对DirectInput场景)
若游戏采用DirectInput读取输入,可将代码注入游戏进程,HookDirectInput8Create或IDirectInputDevice8::GetDeviceState接口,实现输入的捕获与模拟。
- 工具:用
MinHook或EasyHook实现API钩子,在进程内替换输入读取结果。 - 风险:部分反作弊系统会检测进程注入,老游戏一般无反作弊,但需注意兼容性。
3. 优化SendInput参数适配老游戏
部分老DirectX游戏支持SendInput的扫描码(Scan Code)模拟,而非虚拟键码(VK Code)。修改你的SendInput实现,优先使用扫描码:
INPUT input = new INPUT(); input.type = INPUT_KEYBOARD; input.mkhi.ki.wScan = (ushort)MapVirtualKey((uint)key, MAPVK_VK_TO_VSC); // 转换VK到扫描码 input.mkhi.ki.dwFlags = KEYEVENTF_SCANCODE; // 指定用扫描码模拟 SendInput(1, ref input, Marshal.SizeOf(typeof(INPUT)));
该方法不一定对所有老游戏生效,但可优先尝试。
内容的提问来源于stack exchange,提问作者adam sranko

