簡體   English   中英

如何使用 QNetworkAccessManager 作為 QT DLL function 下載文件?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM