Event Loop in Qt-based DLL in non Qt application

I searched the entire network for an answer, but could not find a solution to my problem. Or maybe I did it, but because I start with C ++ / programming / Qt, I did not understand them. The closest question was here. Using a Qt-based DLL in a non Qt application . I tried to use this method, but so far without success.

I am trying to create a DLL, this is the API for our USB device. The library should work with applications other than Qt. I have PIMPL-ed all Qt materials and private classes, so the code below is a single layer under public classes. I use QSerialPort and a lot of SIGNAL / SLOT, so I need a QCoreApplications event loop. The ReaderSerial, where Qt stuff starts, also instantiates another class in which QSerialPort runs in another QThread.

At the moment, my problem is that all error crashes on error: "QTimer can only be used with threads running with QThread"

I think that my Qt-based classes, such as ReaderSerial, do not see the QCoreApp event loop or anything like that. So my question is how to provide a QCoreApplication event loop for my DLL so that all Qt-based classes are created and I can call methods from my DLL.

Thanks so much for the answers.

reader_p.h

class ReaderPrivate { public: ReaderPrivate(); ~ReaderPrivate(); void static qCoreAppExec(); ReaderSerial *readerSerial; void connectReader(std::string comPort); void disconnectReader(); }; 

reader.cpp

 // Private Qt application namespace QAppPriv { static int argc = 1; static char * argv[] = {"API.app", NULL}; static QCoreApplication * pApp = NULL; }; ReaderPrivate::ReaderPrivate() { std::thread qCoreAppThread(qCoreAppExec); qCoreAppThread.detach(); readerSerial = new ReaderSerial; } ReaderPrivate::~ReaderPrivate() { delete readerSerial; } void ReaderPrivate::qCoreAppExec() { if (QCoreApplication::instance() == NULL) { QAppPriv::pApp = new QCoreApplication(QAppPriv::argc, QAppPriv::argv); QAppPriv::pApp->exec(); if (QAppPriv::pApp) delete QAppPriv::pApp; } } void ReaderPrivate::connectReader(std::string comPort) { readerSerial->openDevice(comPort); } void ReaderPrivate::disconnectReader() { readerSerial->closeDevice(); } 

Based on @Kuba Ober's answer, I created a shared library. It took me a while to figure out what was going on and how to make it work, but it still doesn't do what it should. Therefore, I now ask for advice on how to make this code work.

apic.h

 #include "Windows.h" extern "C" { __declspec(dllexport) void WINAPI kyleHello(); } 

apic.cpp

 #include "apic.h" #include "appthread.h" void WINAPI kyleHello() { worker->hello(); } BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) { static AppThread *thread; switch (reason) { case DLL_PROCESS_ATTACH: thread = new AppThread; thread->start(); break; case DLL_PROCESS_DETACH: delete thread; break; default: break; } return TRUE; }; 

appthread.h

 #include <QThread> #include <QCoreApplication> #include <QPointer> #include "worker.h" static QPointer<Worker> worker; class AppThread : public QThread { public: AppThread(); ~AppThread(); // No need for the Q_OBJECT QPointer<QCoreApplication> m_app; void run() Q_DECL_OVERRIDE { std::cout << "\n AppThread::run"; int argc; char *argv; QCoreApplication app(argc, &argv); m_app = &app; std::cout << "\nAppThread::run before Worker"; Worker worker_; worker = &worker_; std::cout << "\nAppThread::run before app.exec"; app.exec(); } //using QThread::wait(); // This wouldn't work here. }; 

appthread.cpp

 #include "appthread.h" AppThread::AppThread() { std::cout << "\n AppThread::ctor"; } AppThread::~AppThread() { std::cout << "\n AppThread::dtor \n"; m_app->quit(); wait(); } 

worker.h

 #include <QObject> #include <QDebug> #include <iostream> class Worker : public QObject { Q_OBJECT Q_INVOKABLE void helloImpl() { std::cout << "I'm alive."; //qDebug() << "I'm alive."; } public: Worker(); void hello(); }; 

worker.cpp

 #include "worker.h" Worker::Worker() { std::cout << "\nWorker::ctor"; hello(); } void Worker::hello() { std::cout << "\nWorker::hello()"; // This is thread-safe, the method is invoked from the event loop QMetaObject::invokeMethod(this, "helloImpl", Qt::QueuedConnection); } 

The conclusion from this is usually:

 AppThread::ctor Worker::hello() AppThread::dtor 

sometimes:

 AppThread::ctor Worker::hello() AppThread::run AppThread::dtor 

sometimes:

 AppThread::ctor Worker::hello() AppThread::dtor QMutex: destroying locked mutex 

GitHub repo: https://github.com/KyleHectic/apic.git

+7
c ++ qt dll event-loop
source share
2 answers

First of all, if you need QCoreApplication , it will always be your QCoreApplication . You should not try to dynamically bind Qt in your DLL if it ultimately takes Qt from an application that is your consumer. There are no guarantees of binary compatibility between these Qt libraries - this will force your consumer to use the same compiler version and Qt binary compatible build. This is, generally speaking, a fantasy.

So, the idea that you need to check for QCoreApplication simply not appropriate for your usage model. You will always need it. All you have to do is start the thread and run the main application there. What is it.

 QPointer<Worker> worker; extern "C" { __declspec(DLLEXPORT) WINAPI VOID kyleHello() { worker->hello(); } } class Worker() : public Q_OBJECT { Q_OBJECT Q_INVOKABLE void helloImpl() { qDebug() << "I'm alive."; } public: void hello() { // This is thread-safe, the method is invoked from the event loop QMetaObject::invokeMethod(this, "helloImpl", Qt::QueuedConnection); } Worker() { hello(); } }; class AppThread : public QThread { // No need for the Q_OBJECT QPointer<QCoreApplication> m_app; void run() Q_DECL_OVERRIDE { int argc; char * argv; QCoreApplication app(argc, &argv); m_app = &app; Worker worker_; worker = &worker_; app.exec(); } using QThread::wait(); // This wouldn't work here. public: AppThread() {} ~AppThread() { m_app->quit(); wait(); } } BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) { static AppThread * thread; switch (reason) { case DLL_PROCESS_ATTACH: thread = new AppThread; thread->start(); break; case DLL_PROCESS_DETACH: delete thread; break; default: break; } return TRUE; } 

The API open to your consumer has several forms:

  • APIs for entries that do not expect results. Inside, you simply send an event to any of your QObject s. You can also use QMetaObject::invokeMethod with Qt::QueuedConnection - this is just a QMetaCallEvent message for the target. Events can be sent to any QObject from any thread, including those not related to QThread-threads.

  • External thread callbacks: Select a separate thread in which the customer provides callbacks. They will be called by one or more QObject living in this thread.

  • Client-thread callbacks: use asynchronous procedure calls that call back in the context of any thread β€” typically where the callback registration function was called. These callbacks are executed when the thread is in an abnormal state.

    If you want to limit yourself to a subset of the warning states in which the message pump operates ( GetMessage is called), you can create a message- only an invisible window , send messages to it and call user callbacks from the window callback function. If you are smart, you can pass QEvent pointers through these messages and pass them to QObject::event in the callback. How you can make a QObject live effectively in a thread with its own event loop and without a Qt loop loop.

  • Blocking APIs that effectively synchronize the calling thread with your thread: use QMetaObject::invokeMethod with Qt::BlockingQueuedConnection . The caller will wait until the slot completes execution in the receiving stream, optionally passing the result.

  • Blocking APIs that use fine-grained blocking. They also synchronize the stream of the calling stream with your stream, but only at the blocking level of certain data structures. They are useful mainly for reading parameters or extracting data - when the overhead of going through an event loop will overshadow the small amount of work you do.

Which APIs you offer depends on your API design criteria.

All APIs must be extern C and must not use C ++ . You can only offer C ++ APIs if you plan to create DLLs using several versions of VS (for example, 2008, 2010, 2012, 2013). Even then, you should not expose Qt to the consumer, as the consumer may still use a binary incompatible version.

+3
source share

QtWinMigrate solves the Qt problem in Win32 or MFC. One of the answers to the question you are referring to) mentions this.

For a Qt library that needs to bind an event loop in DllMain, just use QMfcApp::pluginInstance

+1
source share

All Articles