I was searching the whole web for an answer but didn't find the solution for my problem. Or maybe I did but because I am a beginner to C++/programming/Qt I didn't understand them. The closest thing was a question here Using a Qt-based DLL in a non-Qt application. I tried to use this method but so far unsuccessfully.
I try to create a DLL, it's the API for our USB device. The library should work on non-Qt applications too. I have PIMPL-ed all Qt stuff and private classes so the code below is one layer under the public classes. I am using QSerialPort and a lot of SIGNAL/SLOT so I need the QCoreApplications event loop. The ReaderSerial is where Qt stuff begins it also instantiate another class where the QSerialPort running in a different QThread.
At this moment my problem is the whole thing crashes on error: “QTimer can only be used with threads started with QThread”
I guess my Qt-based Classes like ReaderSerial don't "see" the QCoreApp event loop or something like that. So my question is how to provide the QCoreApplication event loop to my DLL so all Qt-based classes I created will work and I will be able to call methods from my DLL.
Thank you very much for 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 answer I created a shared library. It took me some time to understand what's going on and how to make it work, but it still doesn't do what it should. So I am now asking for advice 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 output 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