【发布时间】:2021-02-23 12:12:08
【问题描述】:
我正在尝试创建一个 QT DLL 以在 InnoSetup 安装程序中使用它(InnoSetup 是用 Delphi Pascal 编写的)。
当从 InnoSetup 调用时,该 DLL 应该具有从 Internet 下载文件的功能。
InnoSetup 调用如下:
procedure downloadFile();
external '_ZN9testClass10doDownloadEv@files:classTest.dll stdcall delayload';
然后我用这个来称呼它:
function InitializeSetup(): Boolean;
begin
Result := True;
ExtractTemporaryFile('classTest.dll');
downloadFile();
end;
我已经在我的 DLL 中使用一个简单的函数进行了尝试,它正在工作。下面是测试函数:
extern "C" __declspec(dllexport) void testClass::testFunction()
{
QFile file("output.txt");
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << "Function call is working!";
file.close();
}
现在,我尝试在我的 DLL 中实现一个函数,该函数将使用 QNetworkAccessManager 和 QThreads 下载文件。可悲的是,由于某种我无法理解的原因,这不起作用。
这是我的 DLL 代码:
-- TESTDLL.H --
#ifndef TESTDLL_H
#define TESTDLL_H
#include <QtCore>
#include "testdll_global.h"
#include "libs/downloader.h"
class TESTCLASS_EXPORT testClass : public QObject
{
Q_OBJECT
public:
void doDownload();
void handleResults();
public slots:
private:
};
#endif // TESTDLL_H
-- TESTDLL_GLOBAL.H --
#ifndef TESTDLL_GLOBAL_H
#define TESTDLL_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(TESTCLASS_LIBRARY)
# define TESTCLASS_EXPORT Q_DECL_EXPORT
#else
# define TESTCLASS_EXPORT Q_DECL_IMPORT
#endif
#endif // TESTDLL_GLOBAL_H
-- TESTDLL.CPP --
#include "testdll.h"
extern "C" __declspec(dllexport) void testClass::doDownload()
{
downloadWorker *ts_testDownloadWorker = new downloadWorker(this);
connect(ts_testDownloadWorker, &downloadWorker::finished, ts_testDownloadWorker, &QObject::deleteLater);
connect(ts_testDownloadWorker, &downloadWorker::resultReady, this, &testClass::handleResults);
ts_testDownloadWorker->Execute();
}
void testClass::handleResults()
{
QFile file("result.txt");
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << "Passed!";
file.close();
}
-- DOWNLOADER.H --
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QtNetwork>
#include <QDebug>
class downloadWorker : public QThread
{
Q_OBJECT
signals:
void resultReady(const QString &s);
public:
downloadWorker(QObject *parent);
~downloadWorker();
void Execute();
void saveToDisk(QString fileName, QByteArray content);
protected:
void run();
private:
bool m_abort;
QNetworkAccessManager *networkMgr;
QNetworkReply *replyNetworkSmall;
public slots:
void startDownload (QString url, QString fileName);
QByteArray prepareDownload(QString &url);
void downloadFinished(QNetworkReply *reply);
void replyNetworkSmallError();
};
#endif // DOWNLOADER_H
-- DOWNLOADER.CPP --
#include "downloader.h"
downloadWorker::downloadWorker(QObject *parent)
: QThread(parent)
{
m_abort = false;
}
downloadWorker::~downloadWorker()
{
m_abort = true;
wait();
}
void downloadWorker::Execute()
{
m_abort = false;
start();
}
void downloadWorker::run()
{
QString result;
startDownload("http://www.google.com", "download.txt");
exec();
emit resultReady(result);
}
void downloadWorker::startDownload (QString url, QString fileName)
{
QByteArray downloadUrl = prepareDownload(url);
saveToDisk(fileName, downloadUrl);
}
QByteArray downloadWorker::prepareDownload(QString &url)
{
QNetworkAccessManager *networkMgr = new QNetworkAccessManager;
connect(networkMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
QNetworkRequest request;
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setUrl(url);
replyNetworkSmall = networkMgr->get(request);
QEventLoop loop;
connect(replyNetworkSmall, SIGNAL(finished()), &loop, SLOT(quit()));
connect(replyNetworkSmall, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(replyNetworkSmallError()));
loop.exec();
QByteArray bts = replyNetworkSmall->readAll();
return bts;
}
void downloadWorker::saveToDisk(QString fileName, QByteArray content)
{
QFile mfile(fileName);
if (!mfile.open(QFile::ReadWrite))
{
mfile.close();
QFile::remove(fileName);
}
else
{
mfile.write(content);
mfile.flush();
mfile.close();
}
}
void downloadWorker::downloadFinished(QNetworkReply *reply)
{
reply->deleteLater();
this->exit();
}
void downloadWorker::replyNetworkSmallError()
{
if(replyNetworkSmall->error())
{
//errorSmallDownload(replyNetworkSmall->errorString());
//downloadError = true;
}
replyNetworkSmall->deleteLater();
}
-- TESTDLL.pro --
QT -= gui
QT += network
TEMPLATE = lib
DEFINES += TESTCLASS_LIBRARY
CONFIG += c++11 dll
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
testdll.cpp \
libs\downloader.cpp
HEADERS += \
testdll_global.h \
testdll.h \
libs\downloader.h
QMAKE_LFLAGS += -Wl,--output-def,testdll.def
# Default rules for deployment.
unix {
target.path = /usr/lib
}
!isEmpty(target.path): INSTALLS += target
如果我尝试调用 doDownload 函数,则没有下载文件,这就像我的 dll 中没有执行循环来实际完成这项工作一样。但是,如果我将此代码转换为应用程序并使用带有 exect 的 main,则文件正在下载。
我还能对上面的代码做些什么来真正管理下载文件?我有什么遗漏吗?
提前谢谢各位!
PS:我完全知道 InnoSetup 实现了自己的文件下载器功能。但是,我想使用我自己的 DLL 中的下载功能;)
解决方案
这是我的问题的解决方案,基于@MSalters 的回答:
-- TESTDLL.H --
#ifndef TESTDLL_H
#define TESTDLL_H
#include <QtCore>
#include "testdll_global.h"
#include "libs/downloader.h"
class testClass : public QObject
{
Q_OBJECT
public:
void doDownload();
void handleResults();
public slots:
private:
};
#endif // TESTDLL_H
-- TESTDLL.CPP --
#include "testdll.h"
namespace QCoreAppDLL
{
static int argc = 1;
static char * argv[] = {(char *)"testdll.cpp", nullptr};
static QCoreApplication * pApp = nullptr;
}
extern "C" TESTCLASS_EXPORT void initDLL()
{
if (!QCoreApplication::instance())
{
QCoreAppDLL::pApp = new QCoreApplication(QCoreAppDLL::argc, QCoreAppDLL::argv);
testClass w;
w.doDownload();
QCoreAppDLL::pApp->exec();
}
}
void testClass::doDownload()
{
downloadWorker *ts_testDownloadWorker = new downloadWorker(this);
connect(ts_testDownloadWorker, &downloadWorker::finished, ts_testDownloadWorker, &QObject::deleteLater);
connect(ts_testDownloadWorker, &downloadWorker::resultReady, this, &testClass::handleResults);
ts_testDownloadWorker->Execute();
}
void testClass::handleResults()
{
if (QCoreAppDLL::pApp)
QCoreAppDLL::pApp->quit();
}
【问题讨论】:
-
"外部'_ZN9testClass10doDownloadEv"。您可能想阅读
extern "C"。 C 没有类或类函数,因此您不会为testClass::testFunction获得一个好的 C 名称。 -
正确,在我修复我的 dll 的主要问题后,我会这样做,干杯!
-
"(char *)"testdll.cpp" 这看起来很危险。命令行解析器删除它处理的参数并不是闻所未闻的,当
argv是可写的(如通常是)。 -
想要抑制“警告:ISO C++11 不允许从字符串文字转换为 'char *'”警告。有没有其他方法可以抑制它?
-
有点跑题了,但为了记录,有一个现有的 Inno Setup 插件可以通过 HTTP(S) 下载文件:mitrichsoftware.wordpress.com/inno-setup-tools/…