【发布时间】:2014-09-21 09:46:05
【问题描述】:
我在整个网络上搜索答案,但没有找到解决问题的方法。或者也许我做到了,但因为我是 C++/编程/Qt 的初学者,所以我不理解它们。 最接近的问题是Using a Qt-based DLL in a non-Qt application。我尝试使用这种方法,但到目前为止没有成功。
我尝试创建一个 DLL,它是我们 USB 设备的 API。该库也应该适用于非 Qt 应用程序。我有 PIMPL 编辑的所有 Qt 东西和私有类,所以下面的代码是公共类下的一层。我正在使用 QSerialPort 和很多 SIGNAL/SLOT,所以我需要 QCoreApplications 事件循环。 ReaderSerial 是 Qt 东西开始的地方,它还实例化另一个类,其中 QSerialPort 在不同的 QThread 中运行。
此时我的问题是整个事情都因错误而崩溃:“QTimer can only be used with threads started with QThread”
我猜像 ReaderSerial 这样的基于 Qt 的类不会“看到”QCoreApp 事件循环或类似的东西。所以我的问题是如何为我的 DLL 提供 QCoreApplication 事件循环,以便我创建的所有基于 Qt 的类都可以工作,并且我将能够从我的 DLL 调用方法。
非常感谢您的回答。
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();
}
根据@Kuba Ober 的回答,我创建了一个共享库。我花了一些时间来了解发生了什么以及如何使它工作,但它仍然没有做它应该做的事情。所以我现在征求意见如何使这段代码工作。
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);
}
这个输出通常是:
AppThread::ctor
Worker::hello()
AppThread::dtor
有时:
AppThread::ctor
Worker::hello()
AppThread::run
AppThread::dtor
有时:
AppThread::ctor
Worker::hello()
AppThread::dtor
QMutex: destroying locked mutex
GitHub 仓库:https://github.com/KyleHectic/apic.git
【问题讨论】:
-
我不确定你是否真的需要事件循环。当用户初始化库时,如何从方法中调用现在在 qCoreAppExec() 中的内容,然后运行线程 (QThread) 并在该线程中实例化 ReaderSerial?您应该有一个在使用之前调用的库的 init。
-
好的,如果我这样做: readerSerial->moveToThread(&readerSerialThread); readerSerialThread.start();我如何调用 ReaderSerial 方法以便它们阻塞?
-
还有一件事,我从 ctor 调用 qCoreAppExec() 并使用用户必须实例化 Reader 类的 api,所以我认为它类似于初始化,但我不确定。
-
构造函数可以,但 readerSerial 不应该在 ctor 中,而是一些用户调用的 init 应该创建一个 QThread 并在其中 readerSerial = new ReaderSerial;
-
moveToThread 几乎总是不需要顺便说一句,测试创建一个线程并打印一些东西以查看线程本身是否工作正常。
标签: c++ qt dll event-loop