想知道钩子的能力可以做什么功能,调用一个钩子的过程是什么。
目前测试用的程序只有一个进程,挂钩至键盘成功后进入消息循环,仅需要在等待键盘事件触发后进处理,等待过程不希望占用过多CPU。
借助win api创建了一个WH_KEYBOARD_LL类型钩子来相应键盘事件:并使用两种方法GetMessage响应和PeekMessage
HHOOK keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0);
if (!keyboardHook) {
std::cout << "挂钩键盘失败" << std::endl;
return -1;
}
else
std::cout << "挂钩键盘成功" << std::endl;
MSG msg;
int LoopCount = 0;
BOOL bRet;
// 消息循环
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
std::cout << "等待接收消息, count:" << LoopCount << "\n";
if (bRet == -1)
{
std::cout << "GetMessage 出错, count:" << LoopCount << "\n";
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
std::cout << "接到消息, count:" << ++LoopCount << "\n";
// 一些处理程序
Process();
}
作为测试的钩子函数为:
LRESULT CALLBACK LowLevelKeyboardProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam){
std::cout << "hook 回调函数\n";
throw std::logic_error("abc");
std::vector<int> a;
a[-1] = 0;
a.at(-1) = 0;
Sleep(500);
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
如上我尝试抛出了一个逻辑错误,又越界访问了vector类型,都没有导致程序跳出或警告(是win忽略了嘛),GetMessage也没有返回异常状态-1。甚至在CallNextHook前还睡眠了0.5s,也没有阻塞键盘输入响应。但是用调试器在该函数内插入断点后,电脑被卡死了。
我的理解是钩子的目标(此处为键盘)产生消息,首先分发给挂钩的进程,然后再继续分发(给文本输入框等)。挂钩进程需要等候并依次处理消息1,消息2,消息3……。
那么问题1:处理消息的过程如果抛出错误会发生什么?系统是如何处理的?我如何自行catch这个错误?
问题2:处理消息的过程可以并发进行吗?在CallNextHook前等待为什么没有阻塞或延迟消息传递?为什么插入断点会导致键盘输入卡死?
问题3:我能在钩子函数里做什么?一些需要消耗时间的处理过程我应该放在消息循环中还是钩子函数中?
最后,基础知识应该补充哪些?这些属于win api应用知识还是操作系统知识?
/*我是分隔线1/
翻MSDN文档时确实翻到了些相关内容
中的注解提到:
此挂钩在安装它的线程的上下文中调用。 调用是通过向安装了挂钩的线程发送消息进行的。 因此,安装挂钩的线程必须具有消息循环。
而实际上在使用GetMessage()方法接收消息时,while循环体内部的
TranslateMessage(&msg);
DispatchMessage(&msg);
并没有被执行,会一直被GetMessage()所阻塞,虽然文档上说处理完一个消息后会返回,但我实际操作中没找到方法使其返回。但功能键盘功能依然正常
而使用PeekMessage()方法才会执行Translate和DispatchMessage(),而且是必须执行后者键盘功能才正常。
所以补充问题4:LowLevelKeyboardProc()可以确定在何时被调用吗?
/*我是分隔线2/
另外MSDN文档中确实提到了超时问题:
挂钩过程处理消息的时间应短于以下注册表项的 LowLevelHooksTimeout 值中指定的数据条目:
HKEY_CURRENT_USER**Control Panel**Desktop
该值以毫秒计。 如果挂钩过程超时,系统会将消息传递到下一个挂钩。 但是,在 Windows 7 及更高版本上,挂钩以静默方式删除,无需调用。 应用程序无法知道挂钩是否已删除。
该条目值为2.5e3ms。