【发布时间】:2018-04-17 21:12:54
【问题描述】:
我正在调试一个使用 Qt 4.8.6 的闭源遗留应用程序 应用程序在自动更新 Windows 10 后出现问题
当问题被触发时,应用程序(以及整个 Windows 桌面)停止接收键盘和鼠标事件,但应用程序继续运行,鼠标光标也会移动。
如果按下Ctrl+Esc 打开开始菜单,一切都会恢复正常,直到在应用程序中执行特定操作再次触发问题。
我将问题追溯到 Qt 的 QWidget::grabMouse(),它在应用程序中的自定义小部件上调用。
跟踪QWidget::grabMouse()的执行,发现问题发生在执行时:
journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0);
它的qJournalRecordProc 看起来像这样:
// The procedure does nothing, but is required for mousegrabbing to work
LRESULT QT_WIN_CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(journalRec, nCode, wParam, lParam);
}
可以在here找到所有定义的QWidget.cpp的完整源代码
谷歌搜索显示SetWindowsHookEx 自 Vista 以来有特殊要求,以阻止恶意软件使用它。该应用程序似乎满足我通过谷歌搜索找到的要求(由受信任的证书签名,尽管它使用 SHA1,安装在“程序文件”中,...)
现在是实际问题:
为什么 Qt 需要
WH_JOURNALRECORD来执行鼠标抓取?我认为通过使用WH_JOURNALRECORD,钩子程序会获取鼠标/键盘事件,这不会影响单个小部件。我修补了QtGui4.dll,所以它不会调用SetWindowsHookEx(以及相关的Unhook)。这修复了应用程序并且没有任何明显的副作用。为什么以这种方式使用
WH_JOURNALRECORD挂钩会停止传递键盘/鼠标事件?我还在钩子程序上设置了一个断点,它似乎永远不会被调用。我还操纵了钩子程序,因此它会使应用程序崩溃(以防这种奇怪的钩子行为弄乱了调试器)并且应用程序没有崩溃,我认为这证实了钩子程序永远不会被调用。
所有这些挂钩的东西看起来像一个真正丑陋的黑客......
显然 Qt 开发人员认识到了这一点,因为 Qt5 的实现不再使用 SetWindowsHookEx...
编辑(澄清一些 cmets):
我已经修改了 QtGui4 库的grabMouse 不调用SetWindowsHookEx 并且不调用相应的UnhookWindowsHookEx。
由于封闭源应用程序显然使用商业许可下的 Qt 库,他们对 Qt 进行了一些封闭源代码修改,这迫使我实际上修补了他们的 QtGui4.dll(将调用SetWindowsHookEx 的部分更改为NOPs。
这解决了应用程序的问题并且不会产生明显的副作用。但我想知道为什么 Qt 开发人员认为有必要首先将钩子(什么都不做)放在那里。是什么让它行为不端。
【问题讨论】:
-
我不喜欢 qJournalRecordProc 上的演员表。
-
嗯... Qt 人们似乎喜欢它。它被定义为 typedef LRESULT (CALLBACK* HOOKPROC)(int code, WPARAM wParam, LPARAM lParam);在 WinUser.h...
-
JournalRecordProc 具有相同的签名......所以这应该没问题
-
如果签名正确,则不需要演员表。 C 强制转换隐藏调用约定错误!
-
当然...但这不是我的代码 :) 这是 Qt 框架本身!