You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"});

疑惑与原理探究

  1. 为什么QEvent可行但Windows消息不行?原本以为QEvent底层是利用Windows消息系统实现跨线程传递的。
  2. 从未调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 15:07:24