C++ 带有WH_键盘LL和keybd_事件的全局键盘挂钩(windows)
我正在尝试编写一个简单的全局键盘钩子程序来重定向一些键。例如,当程序执行时,我按下键盘上的“a”,程序可以禁用它并模拟“b”点击。我不需要图形用户界面,只要一个控制台就足够了(让它继续运行) 我的计划是使用全局钩子捕捉按键输入,然后使用keybd_事件模拟键盘。但是我有一些问题 第一个问题是程序可以正确地阻止'A',但是如果我在键盘上按一次'A',回调函数中的printf会执行两次,keybd_事件也会执行两次。因此,如果我打开一个txt文件,单击一次“a”,就会有两个“B”输入。为什么呢 第二个问题是,为什么使用WH_KEYBOARD_LL的钩子可以在没有dll的情况下在其他进程上工作?在我写这个例子之前,我一直认为我们必须使用dll来创建一个全局钩子C++ 带有WH_键盘LL和keybd_事件的全局键盘挂钩(windows),c++,hook,keyboard-events,keyboard-hook,C++,Hook,Keyboard Events,Keyboard Hook,我正在尝试编写一个简单的全局键盘钩子程序来重定向一些键。例如,当程序执行时,我按下键盘上的“a”,程序可以禁用它并模拟“b”点击。我不需要图形用户界面,只要一个控制台就足够了(让它继续运行) 我的计划是使用全局钩子捕捉按键输入,然后使用keybd_事件模拟键盘。但是我有一些问题 第一个问题是程序可以正确地阻止'A',但是如果我在键盘上按一次'A',回调函数中的printf会执行两次,keybd_事件也会执行两次。因此,如果我打开一个txt文件,单击一次“a”,就会有两个“B”输入。为什么呢 第二
#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);
}
#包括“stdafx.h”
#包括
#定义_WIN32_WINNT 0x050
LRESULT回调LowLevelKeyboardProc(int-nCode、WPARAM-WPARAM、LPARAM-LPARAM)
{
BOOL fEatKeystroke=FALSE;
如果(nCode==HC\U动作)
{
交换机(wParam)
{
案例WM_键控:
案例WM_SYSKEYDOWN:
案例WM_KEYUP:
案例WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p=(PKBDLLHOOKSTRUCT)lParam;
如果(fEatKeystroke=(p->vkCode==0x41)){//将a重定向到b
printf(“你好a\n”);
keybd_事件('B',0,0,0);
keybd_事件('B',0,KEYEVENTF_KEYUP,0);
打破
}
打破
}
}
返回(fEatKeystroke?1:CallNextHookEx(NULL、nCode、wParam、lParam));
}
int main()
{
//安装低级键盘和鼠标挂钩
HHOOK hhkLowLevelKybd=setWindowshookx(WH_KEYBOARD,LL,LowLevelKeyboardProc,0,0);
//保持此应用程序运行,直到我们被告知停止
味精;
while(!GetMessage(&msg,NULL,NULL,NULL)){//此while循环保留钩子
翻译信息(&msg);
发送消息(&msg);
}
UnhookWindowsHookEx(hhkLowLevelKybd);
返回(0);
}
非常感谢 第一个很简单。一个用于向下键,另一个用于向上键。:) 至于为什么它可以在没有DLL的情况下工作,那是因为它是一个全局钩子。与线程特定的进程不同,它是在您自己的进程中执行的,而不是在发生键盘事件的进程中执行。它是通过向安装了钩子的线程发送消息来完成的——这正是您需要消息循环的原因。没有它,你的钩子就无法运行,因为没有人来监听传入的消息
特定于线程的挂钩需要DLL,因为它们是在另一个进程的上下文中调用的。要使其工作,应该将DLL注入该进程。这里的情况并非如此。由于
WM\u KEYDOWN
和WM\u keydup
的原因,回调函数执行了两次。
当您按下键盘上的某个键时,windows会使用WM\u KEYDOWN
消息调用回调函数,当您松开该键时,windows会使用WM\u keydup
消息调用回调函数。这就是回调函数执行两次的原因
您应该将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那里得到了答案
全局钩子不阻止输入,只允许您预览。@CodyGray根据-“…可能返回非零值,以防止系统将消息传递给钩子链的其余部分或目标窗口过程”。对于我来说,阻止系统将消息传递到目标窗口过程看起来就像是阻塞。对于那些试图让它工作,但收到错误的人,HOOK需要HMOD(1428):根据“如果HMOD参数为NULL,dwThreadId参数为零,则可能会发生错误”。因此,您必须指定
hMod
,但在这种情况下,您可以使用任何合法值,因为对于低级钩子,不会注入任何DLL。例如,您可以使用GetModuleHandle(“kernel32.dll”)
。唯一奇怪的是,MSDN说“所有全局钩子函数都必须在库中。”这完全是错误的。所有全局钩子必须在DLL中,除非它是低级钩子。在这方面缺乏文件。这里的语言是C++,但是C语言可以使用低级钩子,因为它们不需要DLL。