非Qt应用调用的Qt共享库中QCoreApplication与事件循环实现
解决方案:在非Qt应用调用的Qt DLL中启用QTimer
要让你的Qt DLL在非Qt应用中正常运行QTimer,核心是自行创建QCoreApplication实例,并将Qt事件循环移至后台线程避免阻塞调用者,同时保证线程安全。以下是修改后的完整实现:
修改后的代码
myqtlib.h(无需改动)
#define LIB_CALLCONV __cdecl #ifdef BUILD_LIB #define LIB_API __declspec(dllexport) #else #define LIB_API __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif LIB_API void LIB_CALLCONV libInit( void ); LIB_API float LIB_CALLCONV libGetValue( void ); #ifdef __cplusplus } #endif
myqtlib.cpp
#include <QCoreApplication> #include <QTimer> #include <QThread> #include <QMutex> #include <QDebug> #include <windows.h> QMutex valueMutex; int Value = 0; // 承载定时器的工作线程,内部运行Qt事件循环 class WorkerThread : public QThread { Q_OBJECT protected: void run() override { QTimer timer; connect(&timer, &QTimer::timeout, this, &WorkerThread::onTimerTriggered); timer.start(500); exec(); // 启动线程专属的事件循环,处理定时器信号 } private slots: void onTimerTriggered() { QMutexLocker locker(&valueMutex); Value++; qDebug() << "*** QtLib *** onTimerTriggered called, setting val to" << Value << "!"; } }; static WorkerThread* workerThread = nullptr; LIB_API void LIB_CALLCONV libInit( void ) { qDebug() << "*** QtLib *** libInit called, QCoreApplication exists:" << (QCoreApplication::instance() != nullptr); // 检查是否已有QCoreApplication,无则创建(必须在主线程执行) if (!QCoreApplication::instance()) { int argc = 0; char** argv = nullptr; new QCoreApplication(argc, argv); qDebug() << "*** QtLib *** Created QCoreApplication instance"; } // 启动承载定时器的后台线程 if (!workerThread) { workerThread = new WorkerThread(); workerThread->start(); qDebug() << "*** QtLib *** Started worker thread with timer"; } } LIB_API float LIB_CALLCONV libGetValue( void ) { QMutexLocker locker(&valueMutex); return static_cast<float>(Value); } // 库卸载时清理资源,避免内存泄漏 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: // 停止并清理工作线程 if (workerThread) { workerThread->quit(); workerThread->wait(); delete workerThread; workerThread = nullptr; } // 销毁自行创建的QCoreApplication delete QCoreApplication::instance(); break; } return TRUE; } // 必须包含moc生成的文件,处理Q_OBJECT宏 #include "myqtlib.moc"
关键实现说明
QCoreApplication实例管理:
在libInit中检查是否已有QCoreApplication实例,没有则创建。QCoreApplication必须在主线程创建,而libInit通常由调用者主线程触发,符合Qt要求。后台事件循环:
将QTimer放入独立的WorkerThread中,在run()方法里启动exec()运行事件循环。这样定时器的信号会被正常处理,且不会阻塞调用者的主线程。线程安全保障:
用QMutex保护全局变量Value,避免后台线程修改与调用者线程读取时的竞态条件。资源清理:
在DllMain的DLL_PROCESS_DETACH分支中,停止工作线程并销毁QCoreApplication,防止内存泄漏。注意不要在DLL_PROCESS_ATTACH中创建QCoreApplication,因为DllMain的执行环境限制较多。
编译注意事项
- 确保Qt项目配置中添加
QT += core,链接Qt核心模块。 - 若使用qmake,需保证项目生成moc文件(Qt Creator会自动处理,手动编译需运行
moc myqtlib.cpp -o myqtlib.moc)。 - 编译架构需与调用者应用一致(32位/64位)。
效果验证
非Qt应用调用libInit后,会输出如下日志,定时器正常触发:
*** QtLib *** libInit called, QCoreApplication exists: false *** QtLib *** Created QCoreApplication instance *** QtLib *** Started worker thread with timer *** QtLib *** onTimerTriggered called, setting val to 1 ! *** QtLib *** onTimerTriggered called, setting val to 2 !
调用libGetValue可安全获取当前计数器值,不会阻塞应用主线程。
内容的提问来源于stack exchange,提问作者jcwinkler
相关产品推荐
相关产品推荐

