昨天碰上一个挺郁闷的问题。我们需要在 Qt 程序里处理某些原生 Windows 消息,本来这应该是个挺简单的事情。Qt 早期版本有一个专门的 winEvent 事件,但是用新版时发现这个函数已经换成了 nativeEvent,如果不需要关注跨平台的话,这个方法也没多大差别。重写该函数后,运行时方法倒是触发了,但从参数获得的却是一些匪夷所思的消息标志,简直百思不得其解。仔细看代码也发现不了什么问题,尝试了多种不同写法,无果。通过网络搜索,发现还有另一种方法是通过消息过滤器,即从 QAbstractNativeEventFilter 继承子类。按照这个路子走倒是正常了。因为时间紧,也没来得及深入思考,把程序直接提交了事。

但下班路上再想一想又觉得不对劲,毕竟 QAbstractNativeEventFilter::nativeEventFilterQWidget::nativeEvent 方法签名是完全一致的,基本上可以判定它们使用的是同一种机制,处理代码也没有什么差别。为什么一个可以,一个不行呢?之前我本能地拒绝往这个方向去考虑,但现在越来越觉得,会不是 Qt 本身的问题造成呢?

思路一开,问题就好定位了。我使用的 Qt 是自己编译的,这样有一个好处是可以从代码层面去调试,确认问题发生在哪里。打开 VS,从调用堆栈一层一层检查参数值:

Qt Call Stack

哈哈,还真的找到问题了。错误发生在 qtbase/src/plugins/platforms/windows/qwindowscontext.cpp, 可以看到,传入的参数 msg 已经是一个指针,但程序员估计是一时手滑,多写了一个取址符(&), 于是传到应用程序的已经不再是一个正常的 MSG* 指针了。

// Send to QWindowSystemInterface
bool QWindowsContext::filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result)
{
    long filterResult = 0;
    if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), &msg, &filterResult)) {
        *result = LRESULT(filterResult);
        return true;
    }
    return false;
}

既然找到了问题,我就想是不是应该到官方提交一个报告或者补丁。找到 Qt 代码仓库看看版本记录,才发现前段时间已经有位仁兄把代码改好了:

Qt git commit

从修改记录看,这个问题是版本 5.11 引进的,而在 5.11.2 和 alpha 版的 5.12.0 中得到了修正。正巧我使用的是 5.11.1 版的 Qt,所以中招了。

知道了这个结果,有空的时候我会下载一个新版的 Qt 5.11.2 来重新编译一下。希望其他使用 Qt 的朋友们也留心这个坑吧。