了解低级鼠标和键盘钩子(win32)

Ste*_* Lu 13 c++ winapi multithreading

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

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
}
Run Code Online (Sandbox Code Playgroud)

所以一切都在这里工作,除非我#define TEST放入Sleep,鼠标变得非常迟钝,如果我突然只允许鼠标每秒更新20次,可能会出现这种情况.没有睡眠,我将CPU固定在100%.但是现在这没关系(如果我使用的话就会消失GetMessage).

根据我的理解,低级钩子通过上下文切换到安装它的进程,然后发送进程某种消息让它执行钩子回调.令我困惑的是,为什么我的程序永远不会打印"msg recvd",但只要我点击鼠标右键,就会打印"鼠标右键/向上".这使我得出结论,我MouseHookProcPeekMessage呼叫期间被调用.它恰好是某种特殊的消息,并PeekMessage返回0.但我仍然需要打电话PeekMessage或一些等价物.

由于我的程序需要做很多事情,我显然不能通过PeekMessage调用另一个函数来减轻我的消息循环(调用的那个),比如50ms返回.我怎样才能多线程化我的程序以保持鼠标响应能力,同时做一些繁重的工作?在多线程win32程序中,仍然只有一个消息队列,对吧?

更新:在阅读了MS的文档后,我想我知道对我来说正确的做法是什么.我应该在我的应用程序中生成一个线程,该线程调用SetWindowsHookEx注册鼠标钩子,然后在它自己的消息循环中坐下来,系统将负责将鼠标更新发送到该线程.它可以随意做任何想做的事情MouseHookProc,我的应用程序的其余部分将独立运行.

Han*_*ant 11

问题是你的消息循环,因为你使用PeekMessage()会烧掉100%的CPU周期.Windows知道如何保持挂钩活动,即使您不轮询消息,使用GetMessage()来解决您的问题.使用Sleep(1)也可以解决您的问题,但这里没有必要.

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


Ole*_*leg 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); 
Run Code Online (Sandbox Code Playgroud)

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

printf("right mouse down\n");
Run Code Online (Sandbox Code Playgroud)

无法工作.

所以我希望你现在不会想到为什么你必须放在MouseHookProcDLL中.

  • 我想这里可能会有一些混乱.目前我或多或少已经从这个主题中找到了我需要的一切:http://stackoverflow.com/questions/2060345/in-what-thread-does-a-low-level-mouse-and-keyboard- hook-callback-run感谢您抽出宝贵时间提供帮助! (2认同)
  • 这是非常有趣的信息!非常有趣的是,`WH_MOUSE_LL`是基因DLL注入规则的一个例子.谢谢!我建议你在http://msdn.microsoft.com/en-us/library/ms644990.aspx上使用"向Microsoft发送有关此主题的评论",建议微软更改一下`SetWindowsHookEx`的小文档.来自http://msdn.microsoft.com/en-us/library/ms644986.aspx的信息. (2认同)
  • @rogerdpack是的.在[here](http://msdn.microsoft.com/en-us/library/ms644986.aspx)的评论中,您可以读到:"但是,WH_MOUSE_LL挂钩不会注入另一个进程**.相反,上下文切换回安装了钩子的进程,并在原始上下文中调用它.然后上下文切换回生成事件的应用程序." 所有其他钩子都必须在目标进程中注入(加载)`HookProc`的代码.它只适用于我在我的回答中描述的DLL. (2认同)