C++ 了解低级键鼠钩子(win32)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3134183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:08:35  来源:igfitidea点击:

Understanding the low-level mouse and keyboard hook (win32)

c++multithreadingwinapi

提问by Steven Lu

I'm trying to capture global mouse and keyboard input.

我正在尝试捕获全局鼠标和键盘输入。

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
}

So everything works here, except if I #define TESTto put in the Sleep, the mouse becomes incredibly sluggish, as might be expected if I suddenly only allow the mouse to update 20 times a second. And without the sleep, I am pegging the CPU at 100%. But that's okay for now (that goes away if I use GetMessage).

所以这里一切正常,除非我#define TEST放入Sleep,鼠标变得非常缓慢,如果我突然只允许鼠标每秒更新 20 次,这可能是预料之中的。没有睡眠,我将 CPU 固定在 100%。但是现在没关系(如果我使用 ,它就会消失GetMessage)。

Now as I understand it, the low-level hooks work by context-switching to the process which installed it, and then sending the process some kind of message to let it execute the hook callback. What confuses me a little, though, is why my program will never print "msg recvd", but it prints "right mouse down/up" whenever i click the right mouse button. This leads me to conclude that my MouseHookProcis being invoked during the PeekMessagecall. It just happens to be some kind of special message and PeekMessagereturns 0. But I still need to call PeekMessageor some equivalent.

现在据我所知,低级钩子通过上下文切换到安装它的进程来工作,然后向进程发送某种消息以让它执行钩子回调。不过,让我有点困惑的是,为什么我的程序永远不会打印“msg recvd”,但每当我单击鼠标右键时,它就会打印“鼠标右键向下/向上”。这使我得出结论,MouseHookProcPeekMessage通话期间调用了 my 。它只是碰巧是某种特殊消息并PeekMessage返回 0。但我仍然需要调用PeekMessage或某种等效的方法。

Since my program needs to do a bunch of things, I clearly can't weigh down my message pumping loop (the one that calls PeekMessage) by calling another function that takes, say 50ms to return. How might I multithread my program to maintain mouse responsiveness while simultaneously doing a little heavy lifting? In a multithreaded win32 program, there is still just one message queue, right?

由于我的程序需要做很多事情,我显然不能通过PeekMessage调用另一个需要 50 毫秒返回的函数来减轻我的消息泵循环(调用)。我如何多线程处理我的程序以保持鼠标响应,同时做一些繁重的工作?在多线程的 win32 程序中,仍然只有一个消息队列,对吗?

Update: After reading up on MS's documentation I think I know what the right thing for me to do is. I should just spawn a thread in my application which calls SetWindowsHookExto register the mouse hook, and then sit around in its own message loop, and the system will take care of sending the mouse updates to this thread. It will be free to do whatever it wants within the MouseHookProc, and the rest of my application will run independently.

更新:在阅读了 MS 的文档后,我想我知道我应该做什么是正确的。我应该在我的应用程序中生成一个线程来调用SetWindowsHookEx注册鼠标钩子,然后坐在它自己的消息循环中,系统将负责将鼠标更新发送到这个线程。它可以自由地在MouseHookProc.

采纳答案by Hans Passant

The problem is your message loop, it burns 100% CPU cycles because you use PeekMessage(). Windows knows how to keep the hook alive even if you don't poll for messages, use GetMessage() to solve your problem. Using Sleep(1) will solve your problem too but is not necessary here.

问题在于您的消息循环,因为您使用 PeekMessage(),它会消耗 100% 的 CPU 周期。即使您不轮询消息,Windows 也知道如何使钩子保持活动状态,请使用 GetMessage() 来解决您的问题。使用 Sleep(1) 也可以解决您的问题,但在这里不是必需的。

Why must SetWindowsHookEx be used with a windows message queue

为什么必须将 SetWindowsHookEx 与 Windows 消息队列一起使用

回答by Oleg

I aksed you whether you place the place MouseHookProcin DLL, because attempts to place it inside an EXE it is a typical error. I made it also many years ago.

我问你是否把这个地方MouseHookProc放在 DLL 中,因为试图把它放在一个 EXE 中是一个典型的错误。很多年前我也做过。

First of all, how you can read in http://msdn.microsoft.com/en-us/library/ms644990.aspx:

首先,如何阅读http://msdn.microsoft.com/en-us/library/ms644990.aspx

SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names.

SetWindowsHookEx 可用于将 DLL 注入另一个进程。32位DLL不能注入64位进程,64位DLL不能注入32位进程。如果应用程序需要在其他进程中使用钩子,则要求32位应用程序调用SetWindowsHookEx向32位进程注入32位DLL,64位应用程序调用SetWindowsHookEx注入64位DLL DLL 转换为 64 位进程。32 位和 64 位 DLL 必须具有不同的名称。

So you must place in a DLL. To be exactly if you want support both 32-bit and 64-bit platforms you have to implement two dlls: one 32-bit and 64-bit DLL. But why? And how SetWindowsHookExworks?

所以你必须放在一个DLL中。确切地说,如果您想要同时支持 32 位和 64 位平台,您必须实现两个 dll:一个 32 位和 64 位 DLL。但为什么?以及如何SetWindowsHookEx运作?

If you execute in an EXE the code like following

如果您在 EXE 中执行如下代码

HINSTANCE hinstDLL = LoadLibrary(TEXT("c:\myapp\syshook.dll"));
HOOKPROC hkprcMouse = (HOOKPROC)GetProcAddress(hinstDLL, "MouseHookProc");
HHOOK hhookMouse = SetWindowsHookEx( 
                    WH_MOUSE_LL,
                    hkprcMouse,
                    hinstDLL,
                    0); 

you give user32.dllrequest to inject your syshook.dllin all other processes on the same windows station (dll will not be injected to services and processes of other users logged through fast user switching). Then user32.dllcall LoadLibraryto the syshook.dllin different processes. Then if the function MouseHookProcwill be called, in will be called in the context of the process which proccess the mouse message. If the process is not a console application the code like

您向user32.dll发出请求,将您的syshook.dll注入到同一 Windows 工作站上的所有其他进程中(dll 不会注入到通过快速用户切换登录的其他用户的服务和进程中)。然后user32.dll中调用LoadLibrarysyshook.dll在不同的进程。然后,如果MouseHookProc将调用该函数,则将在处理鼠标消息的进程的上下文中调用 in。如果进程不是控制台应用程序,则代码如下

printf("right mouse down\n");

can not work.

不能工作。

So I hope now you will undestend why you must place MouseHookProcin a DLL.

所以我希望现在你会明白为什么你必须放置MouseHookProc在一个 DLL 中。

回答by Reed Copsey

Instead of doing:

而不是做:

if (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
    printf("msg recvd\n");
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
Sleep(50);

Switch this to:

将此切换为:

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);

This will allow your app to continue to process all messages in the queue until it's empty (like having no sleep), thengive up some CPU time when the app is "idle".

这将允许您的应用程序继续处理队列中的所有消息,直到队列为空(例如没有睡眠),然后在应用程序“空闲”时放弃一些 CPU 时间。

回答by Dark

MouseHookProcshould reside in dll, otherwise you can not capture "global" input ( http://msdn.microsoft.com/en-us/library/ms997537.aspx)

MouseHookProc应该驻留在 dll 中,否则您无法捕获“全局”输入(http://msdn.microsoft.com/en-us/library/ms997537.aspx

About the loop - you can modify it like this:

关于循环 - 您可以像这样修改它:

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
}