[英]How can i download a file using QNetworkAccessManager as a QT DLL function?
我正在嘗試創建一個 QT DLL 以在 InnoSetup 安裝程序中使用它(InnoSetup 是用 Delphi Pascal 編寫的)。
當從 InnoSetup 調用時,此 DLL 應該具有 function 以從 Internet 下載文件。
InnoSetup 調用是這樣進行的:
procedure downloadFile();
external '_ZN9testClass10doDownloadEv@files:classTest.dll stdcall delayload';
然后我用這個來稱呼它:
function InitializeSetup(): Boolean;
begin
Result := True;
ExtractTemporaryFile('classTest.dll');
downloadFile();
end;
我已經在我的 DLL 中嘗試了一個簡單的 function 並且它正在工作。 這里是測試 function:
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 中實現一個 function,它將使用 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 function,沒有下載文件,就好像我的 dll 中沒有執行循環來實際完成這項工作。 但是,如果我將此代碼轉換為應用程序並使用帶有 exect 的 main,則文件正在下載。
我還能對上述代碼做些什么來實際管理下載文件? 有什么我想念的嗎?
提前謝謝各位!
PS:我完全知道 InnoSetup 有他自己的文件下載器 function 實現。 但是,我想使用我自己的 DLL 下載的 function;)
解決方案
這是我的問題的解決方案,基於@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();
}
如您所述,在普通的 Qt 應用程序中,這將起作用。 那是因為您的QApplication
object 處理QCoreApplication::notify
function。 在您的 DLL 中,您不需要完整的QApplication
但您仍然需要至少一個QCoreApplication
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.