[英]Global keyboard hook with WH_KEYBOARD_LL and keybd_event (windows)
我正在尝试编写一个简单的全局键盘钩子程序来重定向一些键。 例如,当程序执行时,我按键盘上的'a',程序可以禁用它并模拟'b'点击。 我不需要图形ui,只需一个控制台即可(保持运行)
我的计划是使用全局钩子来捕获键输入,然后使用keybd_event来模拟键盘。 但我有一些问题。
第一个问题是程序可以正确地阻止'A',但是如果我在键盘上点击'A'一次,回调函数中的printf会被执行两次,以及keybd_event。 所以,如果我打开一个txt文件,我点击“A”一次,有两个'B'输入。 这是为什么?
第二个问题是为什么使用WH_KEYBOARD_LL的钩子可以在没有dll的情况下在其他进程上工作? 在我写这个例子之前,我认为我们必须使用dll来创建一个全局钩子...
#include "stdafx.h"
#include <Windows.h>
#define _WIN32_WINNT 0x050
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
BOOL fEatKeystroke = FALSE;
if (nCode == HC_ACTION)
{
switch (wParam)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (fEatKeystroke = (p->vkCode == 0x41)) { //redirect a to b
printf("Hello a\n");
keybd_event('B', 0, 0, 0);
keybd_event('B', 0, KEYEVENTF_KEYUP, 0);
break;
}
break;
}
}
return(fEatKeystroke ? 1 : CallNextHookEx(NULL, nCode, wParam, lParam));
}
int main()
{
// Install the low-level keyboard & mouse hooks
HHOOK hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, 0, 0);
// Keep this app running until we're told to stop
MSG msg;
while (!GetMessage(&msg, NULL, NULL, NULL)) { //this while loop keeps the hook
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hhkLowLevelKybd);
return(0);
}
非常感谢!
第一个很容易。 你得到一个用于按键,另一个用于按键。 :)
至于为什么它可以在没有DLL的情况下工作 - 这是因为它是一个全局钩子。 与特定于线程的线程不同,它在您自己的进程中执行,而不是在发生键盘事件的进程中执行。 它是通过消息发送到安装了钩子的线程完成的 - 这正是你需要消息循环的原因。 如果没有它,你的钩子就无法运行,因为没有人可以收听传入的消息。
DLL是特定于线程的钩子所必需的,因为它们是在另一个进程的上下文中调用的。 为此,您的DLL应该注入该进程。 事实并非如此。
由于WM_KEYDOWN
和WM_KEYUP
您的回调函数执行两次。 当您按下键盘的键时,Windows会使用WM_KEYDOWN
消息调用回调函数,当您释放该键时,Windows会使用WM_KEYUP
消息调用回调函数。 这就是你的回调函数执行两次的原因。
您应该将switch语句更改为:
switch (wParam)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (fEatKeystroke = (p->vkCode == 0x41)) //redirect a to b
{
printf("Hello a\n");
if ( (wParam == WM_KEYDOWN) || (wParam == WM_SYSKEYDOWN) ) // Keydown
{
keybd_event('B', 0, 0, 0);
}
else if ( (wParam == WM_KEYUP) || (wParam == WM_SYSKEYUP) ) // Keyup
{
keybd_event('B', 0, KEYEVENTF_KEYUP, 0);
}
break;
}
break;
}
关于你的第二个问题,我想你已经从@Ivan Danilov回答了。
系统调用此函数。每次新的键盘输入事件即将发布到线程输入队列中。 此消息是特例。 您还需要一个DLL来为其他消息创建一个真正的全局钩子。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.