Qt DLL中工作线程向主线程传消息:QEvent可行PostThreadMessage失效
Qt DLL与Delphi GUI跨线程回调问题
问题背景
我有一个C++/Qt编写的DLL,由Delphi GUI应用调用,两者均运行在Windows系统中。该DLL会创建多个工作线程,需要将工作线程的结果通过回调通知Delphi应用,且回调必须在Delphi主线程上下文执行,而非工作线程。因此工作线程需先向主线程发信号,再由主线程调用Delphi的回调。
尝试的两种方案
方案1:PostThreadMessage() 发送Windows消息(失败)
工作线程调用PostThreadMessage()向主线程发送自定义消息,但Delphi主线程从未收到消息。
DLL初始化代码:
void initDll() { /* 调用应用的线程ID(即Delphi GUI线程) */ mainThreadId = GetCurrentThreadId(); if (auto * app = QCoreApplication::instance(); app == nullptr) { /* 尚未创建实例,进行创建 */ DLL_qtApplicationCreated = true; /* 下方是QtMainApp的类声明/定义 */ DLL_qtApplication = new QtMainApp(qArgc, qArgv); /* 添加Windows消息事件过滤器 */ auto * eventFilter = new bcc::dll::WindowsMessageEventFilter(); DLL_qtApplication->installNativeEventFilter(eventFilter); /* 需调用一次processEvents使Qt与Windows事件循环同步 */ QApplication::processEvents(); } }
Windows消息事件过滤器代码:
/** * 过滤器接收原生Windows消息。 * * 附加到Qt应用。 */ class WindowsMessageEventFilter : public QAbstractNativeEventFilter { public: /** * 接收Windows消息。 * * @see QAbstractNativeEventFilter::nativeEventFilter() */ bool nativeEventFilter( const QByteArray & eventType, void * message, qintptr * result) override { assert(eventType.toStdString() == "windows_dispatcher_MSG"s || eventType.toStdString() == "windows_generic_MSG"s); /* 遗憾的是未收到任何事件... */ const auto * msg = static_cast<MSG *>(message); receivedMessages123.emplace_back(*msg); } std::vector<MSG> receivedMessages123; /** 保存所有接收的消息用于后续调试 */ };
工作线程发送消息代码:
PostThreadMessage(mainThreadId, 0x1234, 456, 789);
注:控制台应用调用DLL时,Windows消息能正常接收;但Delphi应用调用时,无任何事件传递。
方案2:QApplication::postEvent() 发送自定义QEvent(成功)
即使在Delphi应用中调用,主线程也能正常接收自定义QEvent,每发送一个事件,eventCount都会递增。
自定义QApplication派生类代码:
class QtMainApp : public QApplication { Q_OBJECT public: QtMainApp( int argc, char * argv[]) : QApplication(argc, argv) { } ~QtMainApp() = default; int eventCount = 0; /** 统计所有传入notify()的事件数量 */ protected: /** 接收所有QEvent并筛选用户事件 */ bool notify( QObject * receiver, QEvent * event) override { if (event->type() == QEvent::User) { /* 此逻辑正常工作 */ eventCount++; } return QApplication::notify(receiver, event); } };
自定义QEvent类代码:
/** * 附带负载的自定义QEvent */ class QUserEvent : public QEvent { public: QUserEvent( const QString & text) : QEvent(QEvent::User) , text(text) { } ~QUserEvent() = default; public: QString text; };
工作线程发送事件代码:
QtMainApp::postEvent(qApp /*接收者*/, new QUserEvent{"this is a test"});
疑惑与原理探究
- 为什么QEvent可行但Windows消息不行?原本以为QEvent底层是利用Windows消息系统实现跨线程传递的。
- 从未调用
QApplication::exec(),Qt事件循环并未运行也未被轮询,但QEvent依然能正常处理,想了解其中原理。
补充编辑
- 编辑1:DLL中使用QImage、QFontDatabase等类,因此必须创建QApplication实例。未使用信号槽机制,因为不需要回调的运行时绑定特性,且额外的MOC/QObject子类化会增加编译时间。
- 编辑2:查看Qt源码发现,Windows系统中Qt会将QEvent实例放入消息队列,通过向启动时创建的隐藏窗口发送
PostMessage唤醒接收线程(对应qeventdispatcher_win.cpp中的qt_create_internal_window()),Windows消息由同一文件中的qt_internal_proc()方法接收。由此推测:要接收Windows消息,需要注册此类隐藏窗口并搭配回调管理器。
内容的提问来源于stack exchange,提问作者Paule
相关产品推荐
相关产品推荐

