如何使用 QNetworkAccessManager 作为 QT DLL 函数下载文件?
Posted
技术标签:
【中文标题】如何使用 QNetworkAccessManager 作为 QT DLL 函数下载文件?【英文标题】:How can i download a file using QNetworkAccessManager as a QT DLL function? 【发布时间】: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/…
【参考方案1】:
正如您所说,在普通的 Qt 应用程序中,这将起作用。那是因为您的 QApplication
对象处理了 QCoreApplication::notify
函数。在您的 DLL 中,您不需要完整的 QApplication
,但您仍然需要至少一个 QCoreApplication
【讨论】:
谢谢,我找到了一种方法来重新实现你的建议,现在它正在工作。发布了固定代码,其他人也可以从中受益。以上是关于如何使用 QNetworkAccessManager 作为 QT DLL 函数下载文件?的主要内容,如果未能解决你的问题,请参考以下文章
如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]
如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?