开发类似ReWASD程序:C++中如何基于窗口句柄阻止键鼠输入?
目标应用专属键鼠输入拦截方案
针对你开发类似ReWASD工具的需求,BlockInput()全局拦截的特性无法满足“仅针对目标应用”的要求,而SetWindowsHookEx()若用全局钩子会影响所有程序,下面是几个精准拦截的可行方案:
1. LowLevel全局钩子+窗口进程过滤
利用WH_KEYBOARD_LL和WH_MOUSE_LL低级别钩子(无需注入目标进程),在钩子回调中判断当前活动窗口所属进程是否为目标应用,是则拦截输入:
实现步骤
- 预先获取目标应用的进程ID(
targetPid),可通过FindWindow+GetWindowThreadProcessId或CreateToolhelp32Snapshot枚举进程得到 - 注册低级别钩子:
HHOOK hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardLowLevelProc, GetModuleHandle(NULL), 0); HHOOK hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseLowLevelProc, GetModuleHandle(NULL), 0);
- 钩子回调中做进程过滤:
LRESULT CALLBACK KeyboardLowLevelProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0) { DWORD currentPid; GetWindowThreadProcessId(GetForegroundWindow(), ¤tPid); if (currentPid == targetPid) { // 拦截键盘输入,返回非0值跳过后续处理 return 1; } } return CallNextHookEx(NULL, nCode, wParam, lParam); } LRESULT CALLBACK MouseLowLevelProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0) { DWORD currentPid; GetWindowThreadProcessId(GetForegroundWindow(), ¤tPid); if (currentPid == targetPid) { // 拦截鼠标输入 return 1; } } return CallNextHookEx(NULL, nCode, wParam, lParam); }
- 注意:需要保持消息循环运行,钩子才能持续生效;若程序退出,钩子自动失效。
2. 原始输入(Raw Input)拦截
通过注册原始输入设备,接收物理键鼠的原始输入消息,在窗口过程中针对目标应用拦截:
实现步骤
- 注册原始输入设备(需在窗口创建后执行):
RAWINPUTDEVICE rid[2]; // 注册键盘输入,指定接收消息的窗口句柄hWnd rid[0].usUsagePage = 0x01; rid[0].usUsage = 0x06; rid[0].dwFlags = RIDEV_INPUTSINK; rid[0].hwndTarget = hWnd; // 注册鼠标输入 rid[1].usUsagePage = 0x01; rid[1].usUsage = 0x02; rid[1].dwFlags = RIDEV_INPUTSINK; rid[1].hwndTarget = hWnd; RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE));
- 在窗口过程中处理
WM_INPUT消息:
case WM_INPUT: { UINT dataSize = 0; GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER)); LPBYTE rawBuffer = new BYTE[dataSize]; if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, rawBuffer, &dataSize, sizeof(RAWINPUTHEADER)) == dataSize) { RAWINPUT* pRawInput = (RAWINPUT*)rawBuffer; // 判断输入类型为键盘或鼠标 if (pRawInput->header.dwType == RIM_TYPEKEYBOARD || pRawInput->header.dwType == RIM_TYPEMOUSE) { DWORD currentPid; GetWindowThreadProcessId(GetForegroundWindow(), ¤tPid); if (currentPid == targetPid) { // 拦截输入,不传递给目标应用 delete[] rawBuffer; return 0; } } } delete[] rawBuffer; break; }
- 优势:直接获取硬件原始输入,避免被其他钩子干扰;无需注入目标进程。
3. 目标进程线程钩子(需DLL注入)
针对目标应用的特定线程设置钩子(WH_KEYBOARD/WH_MOUSE),仅拦截该线程的输入,精准度最高:
实现步骤
- 编写包含钩子回调函数的DLL,回调中直接拦截输入:
extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0) { // 拦截当前线程的键盘输入 return 1; } return CallNextHookEx(NULL, nCode, wParam, lParam); }
- 在主程序中获取目标应用的线程ID(通过
EnumProcessThreads枚举目标进程的所有线程):
DWORD targetThreadId; // 枚举目标进程线程逻辑,将目标线程ID赋值给targetThreadId
- 设置线程钩子:
HMODULE hHookDll = LoadLibrary(L"HookDll.dll"); HOOKPROC pKeyboardProc = (HOOKPROC)GetProcAddress(hHookDll, "KeyboardProc"); HHOOK hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, pKeyboardProc, hHookDll, targetThreadId);
- 注意:钩子DLL会被注入到目标进程空间,需处理32/64位兼容性问题;若目标进程退出,钩子自动失效。
内容的提问来源于stack exchange,提问作者user21327761
相关产品推荐
相关产品推荐

