我正在开发一个使用键盘挂钩的程序。但是,当运行该程序的 PC 只是轻微过载时,它会导致 Windows 断开挂钩与程序的连接,导致它不再响应击键。
有没有办法通过使用不同的架构(可能涉及管道)来防止这种情况,或者甚至更好地提出解决完全相同问题的不同方法?
我正在开发一个使用键盘挂钩的程序。但是,当运行该程序的 PC 只是轻微过载时,它会导致 Windows 断开挂钩与程序的连接,导致它不再响应击键。
有没有办法通过使用不同的架构(可能涉及管道)来防止这种情况,或者甚至更好地提出解决完全相同问题的不同方法?
您无法“检测”到这一点,而且您绝对不需要。您所描述的是一项功能,特别是在 Windows 7 中引入的一项功能,用于保护您的系统免受恶意应用程序的侵害。
因此,适用的文档对其进行了描述(特别注意粗体部分):
钩子程序处理消息的时间应该少于
LowLevelHooksTimeout
在以下注册表项的值中指定的数据条目:HKEY_CURRENT_USER\Control Panel\Desktop
该值以毫秒为单位。如果钩子过程超时,系统将消息传递给下一个钩子。但是,在 Windows 7 及更高版本上,该挂钩会在不被调用的情况下被静默删除。应用程序无法知道挂钩是否被移除。
这里的解决方案肯定不是想出一种方法来“检测”钩子何时被卸载并重新安装。当操作系统第一次卸载钩子时,您应该已经发现自己做错了。
实际的解决方案是重新设计您的应用程序以更快地从挂钩过程中返回。理想情况下,您应该几乎立即返回。如果您需要运行某种类型的密集计算以响应低级消息(我无法想象您为什么会这样做),那么您应该存储您收到的信息,从挂钩过程返回,并在稍后(可能在单独的线程上)。
事实上,这几乎正是文档继续建议的内容:
注意: 调试挂钩无法跟踪此类低级键盘挂钩。如果应用程序必须使用低级挂钩,它应该在专用线程上运行挂钩,将工作传递给工作线程,然后立即返回。在应用程序需要使用低级挂钩的大多数情况下,它应该监视原始输入。这是因为原始输入可以比低级挂钩更有效地异步监视针对其他线程的鼠标和键盘消息。有关原始输入的更多信息,请参阅原始输入。
我不太确定键盘挂钩总是受到指责。我们似乎都同意,在理想或平均条件下,一切都应该响应。但是在从启动到关闭的整个生命周期中,钩子还必须在一些最坏的情况下生存下来。在我的公司,我们编写了一些键盘钩子,它们尽可能轻量级和异步,但它们仍然偶尔会出现断开连接。
作为用户,我每天要输入数万个字符。我最多每月重启一次,有时我会因为跳过 Windows 更新而感到内疚。有了暂停和休眠等选项,我认为我并不孤单。作为开发人员,我必须确保钩子从头到尾一直运行,无论系统发生什么。
通常我的系统反应灵敏。但是可能会有一些短暂的、特殊的时刻,它会跪倒在地,甚至 Windows Aero 也会被关闭。现在它非常活泼。但是,如果我按下 Show Desktop 按钮,在我的 65 个左右打开的窗口全部折叠期间,鼠标将冻结至少一秒钟。如果我在其中一个时刻按下一个键怎么办?
如果 Windows 可以冻结鼠标一秒钟,如果 Windows 甚至可以在短暂的重负载时刻关闭 Aero,为什么不能允许键盘挂钩在类似的异常过载时间内存活?相反,由于一个特殊的时刻,Windows 拔掉了插头,默默地影响了剩余的计算体验,直到系统关闭。在 200 毫秒内仅影响我们每天按下的数千次按键中的一个按键,就在那几分之一秒内,我,用户,必须重新启动系统,因为这是我理解的唯一方法,它将带回键盘宏或任何实用程序我依靠我的富有成效的工作。
即使保证可以防止上述情况(最坏情况的经验似乎表明并非如此),我也不确定是否可以在单独的线程中轻松完成所有操作。例如,假设用户设置了一个热键来启动应用程序。从当前上下文(前台窗口、权限等)启动应用程序难道没有理由吗?用户实际期望并接受延迟难道不是合理的吗,因为他知道,使用键盘,他只是开始了一些需要更长时间的事情?我不知道这个例子在技术上是否合理,但我想说明有时由于已知事件而可能无法接受的事情是如何被接受的。
键盘钩子对很多事情都非常有用,从宏到纠错再到启动事物,Windows 7 中引入的这种行为将好的和坏的、可接受的和不可接受的、平均的和例外的都放在同一个篮子。这伤害了用户和开发人员,因为高质量的键盘钩子可能会被杀死。想象一下,当您的应用程序在正常条件下运行良好时没有官方解决方案,但在某些重负载(或其他莫名其妙的)情况下被随机杀死,这是一场多么可怕的噩梦。
用一个问题来结束这个问题,有谁知道 Windows 8 下的状态是什么,有什么变化吗?
Cody Gray's response is excellent, but FWIW, and purely for testing purposes, you may be able to detect a disconnected hook.
When your hook proc is invoked, store the current tick count in a variable accessible to the main thread. In the main thread, periodically call GetLastInputInfo
. It will give you the tick count for the last input event in the system (not just in your process). If the value provided by GetLastInputInfo
is significantly later (newer) than your last hook proc tick count, it's a good guess that hook has been disconnected.
我认为您的 hook 中有一些“性能不佳”的代码。这就是 make 稍微过载的原因。
“它会导致 Windows 断开挂钩与程序的连接”
您的钩子中是否出现任何错误并且您不处理它?
AFAIK,如果它自己运行良好,Windows 不会断开挂钩。
尝试提高申请流程的优先级。