我有一个小Python脚本,它收集有关我在Windows机器上使用键盘和鼠标的统计信息。
我通过windll.user32.SetWindowsHookExA(win32con.WH_MOUSE_LL, mouse_pointer, win32api.GetModuleHandle(None), 0)
设置了它。我写这篇文章是为了让你看到我正在使用的API。它也可以是一个c++程序。
它运行得很好,只有一个例外:当我使用Android Studio编译应用程序时,我的4核酷睿i7在所有内核上的最高值为100%,鼠标和操作系统通常会变得没有响应,这是Android Studio的一个已知问题。
通常,当如此高的CPU负载持续几秒钟(4+)时,WH_MOUSE_LL挂钩会被操作系统分离,就好像它在说"不,我们没有时间给你,我们正在注销你"
我不怪Python,因为我还有一个名为PowerMixer的音量管理器,它显然也注册了一个鼠标挂钩,这样它就可以跟踪下任务栏上的鼠标滚轮动作(explorer.exe Shell_RayWnd),而且这个应用程序在编译后也失去了使用鼠标挂钩的能力。
此外,当我在编译并不断滚动滚轮(只是为了好玩)时,计算机开始发出嘟嘟声,就像在非常低级别的系统中发出嘟嘟声一样,然后键盘挂钩也会被注销。
我如何通过Windows API检测我的挂钩是否分离/解除挂钩?我不需要Python特定的答案,只需要了解使用Win32 API处理此问题的正确方法。
SetWindowsHookEx文档的备注部分说:
如果挂钩过程超时,系统会将消息传递给下一个钩子。然而,在Windows7及更高版本上,钩子是无声的在未被调用的情况下删除。应用程序无法知道挂钩是否已卸下。
我在这里运气不好吗?
更新:几周后,我只想通知您,Hans Passan的建议非常有效。我推荐这个。虽然10.000毫秒看起来有点高,但我已经将其设置为这个值,并且没有负面影响。这是在几分钟内解决整个系统问题的方法。
我如何通过Windows API检测我的挂钩是否分离/解除挂钩?
没有直接的方法,您自己的问题引用了支持它的MSDN文档:
如果钩子过程超时,系统会将消息传递给下一个钩子。但是,在Windows7及更高版本中,钩子会在不被调用的情况下自动移除。应用程序无法知道钩子是否已移除。
然而,间接的方法可能是让挂钩跟踪上次调用它的时间,然后主应用程序/脚本可以定期调用GetLastInputInfo()
,并将该时间与跟踪的时间进行比较。如果差异显著高于存储在注册表中的挂钩超时(请参阅文档),那么很可能您的挂钩已经消失。
以下MSDN博客解释了挂钩超时的详细信息:
Windows 7 上的全局挂钩丢失
但是,最重要的是,它指出:
我的建议是,应尽可能避免低级挂钩。如果你正在监视击键(而不是试图阻止它们),你可以通过原始输入获得键盘输入。这比挂钩更轻,不会影响其他应用程序的响应能力,并且如果应用程序没有响应能力,也不会关闭。
这与微软自己在LowLevelMouseProc()
和LowLevelKeyboardProc()
文档中的建议相同:
如果应用程序必须使用低级挂钩,那么它应该在一个专用线程上运行挂钩,该线程将工作传递给工作线程,然后立即返回在大多数应用程序需要使用低级挂钩的情况下,它应该监视原始输入。这是因为原始输入可以异步监视针对其他线程的鼠标和键盘消息,这比低级挂钩更有效。有关原始输入的详细信息,请参见原始输入。