我正在尝试捕获全局鼠标和键盘输入。
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
if (wParam == WM_RBUTTONDOWN) printf("right mouse down\n");
if (wParam == WM_RBUTTONUP) printf("right mouse up\n");
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
HHOOK mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
while(true) {
MSG msg;
if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
printf("msg recvd\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#ifdef TEST
Sleep(50);
#endif
}
所以一切都在这里工作,除非我#define TEST
放入Sleep
,鼠标变得非常迟钝,如果我突然只允许鼠标每秒更新20次,可能会出现这种情况。没有睡眠,我将CPU固定在100%。但是现在没关系(如果我使用GetMessage
,那就消失了。)
现在据我所知,低级钩子通过上下文切换到安装它的进程,然后发送进程某种消息让它执行钩子回调。令我困惑的是,为什么我的程序永远不会打印“msg recvd”,但只要我点击鼠标右键,就会打印“鼠标右键/向上”。这使我得出结论,MouseHookProc
调用期间我的PeekMessage
被调用。它恰好是某种特殊消息,而PeekMessage
返回0.但我仍然需要调用PeekMessage
或等价物。
由于我的程序需要做很多事情,所以我显然不能通过调用另一个需要50ms返回的函数来权衡我的消息循环(调用PeekMessage
的循环)。我怎样才能多线程化我的程序以保持鼠标响应能力,同时做一些繁重的工作?在多线程win32程序中,仍然只有一个消息队列,对吧?
更新:在阅读了MS的文档后,我想我知道对我来说正确的做法是什么。我应该在我的应用程序中生成一个线程,它调用SetWindowsHookEx
来注册鼠标钩子,然后在它自己的消息循环中坐下来,系统将负责将鼠标更新发送到这个线程。它可以在MouseHookProc
内随意做任何事情,我的应用程序的其余部分将独立运行。
答案 0 :(得分:10)
问题是你的消息循环,因为你使用PeekMessage()会烧掉100%的CPU周期。 Windows知道如何保持挂钩活动,即使您不轮询消息,使用GetMessage()来解决您的问题。使用Sleep(1)也可以解决您的问题,但这里没有必要。
Why must SetWindowsHookEx be used with a windows message queue
答案 1 :(得分:6)
我想你是否将地点MouseHookProc
放在DLL中,因为试图将它放在EXE中这是一个典型的错误。我也是在很多年前做的。
首先,您如何阅读http://msdn.microsoft.com/en-us/library/ms644990.aspx:
SetWindowsHookEx可用于注入 一个DLL进入另一个进程。一个32位 DLL无法注入64位 进程,一个64位DLL不能 注入32位进程。如果 应用程序需要使用钩子 在其他过程中,它是必需的 一个32位的应用程序调用 SetWindowsHookEx注入一个32位 DLL进入32位进程,和 64位应用程序调用 SetWindowsHookEx注入一个64位 DLL进入64位进程。 32位 和64位DLL必须有不同 名。
所以你必须放在DLL中。确切地说,如果您想要支持32位和64位平台,则必须实现两个dll:一个32位和64位DLL。但为什么? SetWindowsHookEx
如何运作?
如果您在EXE中执行如下代码
HINSTANCE hinstDLL = LoadLibrary(TEXT("c:\\myapp\\syshook.dll"));
HOOKPROC hkprcMouse = (HOOKPROC)GetProcAddress(hinstDLL, "MouseHookProc");
HHOOK hhookMouse = SetWindowsHookEx(
WH_MOUSE_LL,
hkprcMouse,
hinstDLL,
0);
你给 user32.dll 请求在同一个windows工作站的所有其他进程中注入你的 syshook.dll (dll不会被注入到其他服务和进程中用户通过快速用户切换登录)。然后 user32.dll 在不同的进程中调用{em> syshook.dll LoadLibrary
。然后,如果将调用函数MouseHookProc
,则将在处理鼠标消息的进程的上下文中调用in。如果进程不是控制台应用程序,则代码如
printf("right mouse down\n");
无法正常工作。
所以我希望你现在不会忘记为什么必须将MouseHookProc
放在DLL中。
答案 2 :(得分:3)
而不是:
if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
printf("msg recvd\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(50);
将其切换为:
while (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
// Add this potentially...
if (msg.message == WM_QUIT)
break;
printf("msg recvd\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
这将允许您的应用继续处理队列中的所有消息,直到它为空(如没有睡眠),然后在应用程序“空闲”时放弃一些CPU时间。
答案 3 :(得分:3)
MouseHookProc
应该驻留在dll中,否则您无法捕获“全局”输入(http://msdn.microsoft.com/en-us/library/ms997537.aspx)
关于循环 - 您可以像这样修改它:
while(true) {
MSG msg;
while (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
printf("msg recvd\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#ifdef TEST
DoStuff();
Sleep(50);
#endif
}