源码 #pragma once
#include <QObject>
#include "commondefine.h"
#include "libpublic.h"
#define VISION_CONFIG LibPublic::GetCurrentPath() + "/vision.ini"
INI_BEGIN(Updater, VISION_CONFIG)
INI_PARAM(Vision, QString, VisionNo, "0.0")
INI_END
class UiMainWindow;
class CUpdater : public QObject
{
Q_OBJECT
public:
Kernel(CUpdater);
//************************************
// 作者:
// 日期: 2022/04/14
// 函数: setAppName
// 参数: const QString & name
// 返回: void
// 功能: APP名称
//************************************
void setAppName(const QString& name);
//************************************
// 作者:
// 日期: 2022/04/14
// 函数: setAppPath
// 参数: const QString & path
// 返回: void
// 功能: APP路径 默认当前路径
//************************************
void setAppPath(const QString& path);
//************************************
// 作者:
// 日期: 2022/04/14
// 函数: setServerPath
// 参数: const QString & serverpath
// 返回: void
// 功能: 更新服务器路径
//************************************
void setServerPath(const QString& serverpath);
//************************************
// 作者:
// 日期: 2022/04/16
// 函数: setUpdateFileIndex
// 参数: const QString & fileindex
// 返回: void
// 功能: 更新包名称索引
//************************************
void setUpdateFileIndex(const QString& fileindex);
//************************************
// 作者:
// 日期: 2022/04/14
// 函数: findUpdateFileList
// 参数: float curvision
// 返回: bool
// 功能: 根据版本号查找更新包
//************************************
bool findUpdateFileList();
//************************************
// 作者:
// 日期: 2022/04/14
// 函数: handleUpdate
// 返回: bool
// 功能: 处理更新
//************************************
void handleUpdate();
//************************************
// 作者:
// 日期: 2022/04/16
// 函数: killApp
// 返回: void
// 功能: 杀死进程
//************************************
void killApp();
//************************************
// 作者:
// 日期: 2022/04/16
// 函数: openApp
// 返回: void
// 功能: 打开进程
//************************************
void openApp();
void runUpdateBat();
private:
//************************************
// 作者:
// 日期: 2022/04/14
// 函数: copyUpdateFile
// 参数: const QString & fromFileName
// 参数: const QString & toFileName
// 返回: bool
// 功能: 拷贝文件 带进度条
//************************************
bool copyFileWithProcess(const QString& fromfilename, const QString& tofilename);
bool ExtractsFileWithProcess(const QString& sourcefile, const QString& targetpath);
bool copyDirWithProcess(const QStringList& lstupdatefile);
//************************************
// 作者:
// 日期: 2022/04/17
// 函数: saveVision
// 函数: loadVision
// 返回: void
// 功能: 版本号
//************************************
void saveVision();
void loadVision();
Q_SIGNALS:
//************************************
// 作者:
// 日期: 2022/04/17
// 函数: signalTotalProcessRange
// 函数: signalTotalProcessRange
// 参数: int min
// 参数: int max
// 参数: int value
// 返回: void
// 功能: 总进度条
//************************************
void signalTotalProcessRange(int min, int max);
void signalTotalProcessValue(int value);
//************************************
// 作者:
// 日期: 2022/04/17
// 函数: signalDetailProcessRange
// 函数: signalDetailProcessRange
// 参数: int min
// 参数: int max
// 参数: int value
// 返回: void
// 功能: 详情进度条
//************************************
void signalDetailProcessRange(int min, int max);
void signalDetailProcessValue(int value);
//************************************
// 作者:
// 日期: 2022/04/17
// 函数: signalTotalMessage
// 参数: const QString & mes
// 返回: void
// 功能: 信息
//************************************
void signalTotalMessage(const QString& mes);
void signalDetailMessage(const QString& mes);
private:
CUpdater(QObject *parent = nullptr);
~CUpdater();
//APP名称
QString m_strAppname;
//APP路径 默认当前路径
QString m_strAppPath;
//更新服务器路径
QString m_strServerPath;
//更新包名称
QString m_strFileIndex;
//当前版本号
QString m_strCurVision;
//需要更新的版本号和文件
QMap<QString, QString> m_mapVisionPath;
//多线程
QFutureWatcher<bool> * m_pWatcher;
//窗口
UiMainWindow * m_pMainWindow;
};
#include "cupdater.h"
#include "uimainwindow.h"
#include <QtConcurrent>
#include <windows.h>
//************************************
// 作者:
// 日期: 2022/04/15
// 函数: ExtractsFilesFromArchive
// 参数: const QString & sourcefile
// 参数: const QString & targetpath
// 返回: int
// 功能: 使用7z解压
//************************************
int ExtractsFilesFromArchive(const QString& sourcefile, const QString& targetpath)
{
int ret = 0;
QProcess p;
QString command = "7z.exe";
QStringList args;
args.append("x");
args.append(sourcefile);
args.append(QString::fromLocal8Bit("-o%1").arg(targetpath));
args.append("-aoa");
ret = p.execute(command, args);
return ret;
}
CUpdater::CUpdater(QObject *parent)
: QObject(parent), m_pMainWindow(nullptr)
{
m_strAppPath = LibPublic::GetCurrentPath();
m_strFileIndex = "Update_";
loadVision();
}
CUpdater::~CUpdater()
{
if (m_pMainWindow)
{
delete m_pMainWindow;
m_pMainWindow = nullptr;
}
if (m_pWatcher)
{
delete m_pWatcher;
m_pWatcher = nullptr;
}
}
void CUpdater::setAppName(const QString& name)
{
m_strAppname = name;
if (!m_strAppname.endsWith(".exe"))
{
m_strAppname.append(".exe");
}
qDebug() << QString::fromLocal8Bit("设置APP名称 %1")
.arg(m_strAppname)
.toUtf8().data();
}
void CUpdater::setAppPath(const QString& path)
{
m_strAppPath = path;
if (m_strAppPath.isEmpty())
{
m_strAppPath = LibPublic::GetCurrentPath();
}
qDebug() << QString::fromLocal8Bit("设置APP路径 %1")
.arg(m_strAppPath)
.toUtf8().data();
}
void CUpdater::setServerPath(const QString& serverpath)
{
m_strServerPath = serverpath;
qDebug() << QString::fromLocal8Bit("设置服务器地址 %1")
.arg(m_strServerPath)
.toUtf8().data();
}
void CUpdater::setUpdateFileIndex(const QString& fileindex)
{
m_strFileIndex = fileindex;
if (m_strFileIndex.isEmpty())
{
m_strFileIndex = "Update_";
}
qDebug() << QString::fromLocal8Bit("设置更新包名称索引 %1")
.arg(m_strFileIndex)
.toUtf8().data();
}
bool CUpdater::findUpdateFileList()
{
emit signalDetailProcessRange(0, 0);
emit signalDetailProcessValue(0);
emit signalDetailMessage(QString::fromLocal8Bit("检索更新目录..."));
m_mapVisionPath.clear();
//获取压缩包
QFileInfoList lstfileinfo = LibPublic::GetPathFileInfos(m_strServerPath, "*.zip");
emit signalDetailProcessRange(0, lstfileinfo.count());
emit signalDetailProcessValue(0);
emit signalDetailMessage(QString::fromLocal8Bit("获取更新文件..."));
//筛选压缩包
int i = 1;
for (auto fileinfo : lstfileinfo)
{
emit signalDetailProcessValue(i++);
QString basename = fileinfo.baseName();
QString path = fileinfo.absoluteFilePath();
qDebug() << QString::fromLocal8Bit("更新服务器目录下的文件 %1")
.arg(path)
.toUtf8().data();
emit signalDetailMessage(QString::fromLocal8Bit("获取更新文件...%1").arg(path));
QStringList tmplst = basename.split("_");
if (basename.contains(m_strFileIndex)
&& tmplst.count() > 1)
{
//记录比当前版本号大的文件信息
auto vision = tmplst.at(1);
if (vision.toFloat() > m_strCurVision.toFloat())
{
m_mapVisionPath[vision] = path;
qDebug() << QString::fromLocal8Bit("记录需要更新的文件 %1")
.arg(path)
.toUtf8().data();
emit signalDetailMessage(QString::fromLocal8Bit("记录需要更新文件...%1").arg(path));
}
}
}
emit signalDetailMessage(QString::fromLocal8Bit("检测更新文件完成..."));
return !m_mapVisionPath.isEmpty();
}
void CUpdater::handleUpdate()
{
if (!m_pMainWindow)
{
m_pMainWindow = new UiMainWindow;
}
m_pMainWindow->setWindowTitle(QString::fromLocal8Bit("自动更新 %1").arg(m_strAppname));
m_pMainWindow->show();
LibPublic::Sleep(1000);
QFuture<bool> future = QtConcurrent::run([&]()->bool {
bool ret = false;
emit signalTotalProcessRange(0, 6);
emit signalTotalProcessValue(0);
emit signalTotalMessage(QString::fromLocal8Bit("检查更新文件..."));
//1.检查更新文件 返回是否需要更新
ret = findUpdateFileList();
if (!ret)
{
emit signalDetailMessage(QString::fromLocal8Bit("未发现需要更新的压缩包"));
return ret;
}
emit signalTotalProcessValue(1);
emit signalTotalMessage(QString::fromLocal8Bit("拷贝更新文件至临时文件夹..."));
//2.拷贝更新文件 至临时文件夹
QStringList tmplstupdatefile;
for (auto itmap = m_mapVisionPath.constBegin(); itmap != m_mapVisionPath.constEnd(); ++itmap)
{
QString fromfile = itmap.value();
QString tofile = QDir::tempPath() + QDir::separator() + QFileInfo(fromfile).fileName();
ret = copyFileWithProcess(fromfile, tofile);
qDebug() << QString::fromLocal8Bit("拷贝更新文件至临时文件夹 %1 %2")
.arg(fromfile)
.arg(ret)
.toUtf8().data();
if (!ret)
{
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件失败 %1").arg(fromfile));
return ret;
}
//需要更新的文件
tmplstupdatefile.append(tofile);
}
emit signalTotalProcessValue(2);
emit signalTotalMessage(QString::fromLocal8Bit("解压文件至临时文件夹..."));
//3.解压文件
for (auto updatefile : tmplstupdatefile)
{
QString sourcefile = updatefile;
QString targetpath = QDir::tempPath() + QDir::separator() + QFileInfo(updatefile).baseName();
//解压文件
ret = ExtractsFileWithProcess(sourcefile, targetpath);
qDebug() << QString::fromLocal8Bit("解压文件至临时文件夹 %1 %2")
.arg(sourcefile)
.arg(ret)
.toUtf8().data();
if (!ret)
{
emit signalDetailMessage(QString::fromLocal8Bit("解压文件失败 %1").arg(sourcefile));
return ret;
}
}
emit signalTotalProcessValue(3);
//4.杀死进程
killApp();
emit signalTotalProcessValue(4);
emit signalTotalMessage(QString::fromLocal8Bit("更新文件..."));
//5.更新文件
ret = copyDirWithProcess(tmplstupdatefile);
if (!ret)
{
emit signalDetailMessage(QString::fromLocal8Bit("更新文件失败\r\n %1").arg(tmplstupdatefile.join("\r\n")));
return ret;
}
emit signalTotalProcessValue(5);
emit signalTotalMessage(QString::fromLocal8Bit("记录版本号...%1").arg(m_mapVisionPath.lastKey()));
//6.记录版本号
m_strCurVision = m_mapVisionPath.lastKey();
saveVision();
//7.运行脚本
emit signalTotalProcessValue(6);
emit signalTotalMessage(QString::fromLocal8Bit("运行更新脚本...%1"));
runUpdateBat();
emit signalTotalMessage(QString::fromLocal8Bit("完成自动更新..."));
emit signalDetailMessage(QString::fromLocal8Bit(""));
emit signalDetailProcessRange(0, 100);
emit signalDetailProcessValue(100);
return ret;
});
m_pWatcher = new QFutureWatcher<bool>();
connect(m_pWatcher, &QFutureWatcher<bool>::finished, this, [&]() {
if (!m_pWatcher->result())
{
emit signalTotalMessage(QString::fromLocal8Bit("自动更新失败..."));
emit signalDetailProcessRange(0, 100);
emit signalDetailProcessValue(0);
}
m_pMainWindow->close();
//重新启动
openApp();
//退出
qApp->quit();
});
m_pWatcher->setFuture(future);
}
void CUpdater::killApp()
{
QProcess p;
QString strCmd = "taskkill /f /im " + m_strAppname;
auto ret = p.execute(strCmd);
qDebug() << QString::fromLocal8Bit("杀死进程 %1 %2")
.arg(strCmd)
.arg(ret)
.toUtf8().data();
}
void CUpdater::openApp()
{
QString appnamepath = m_strAppPath + QDir::separator() + m_strAppname;
QString workpath = QFileInfo(appnamepath).absolutePath();
//ShellExecuteA(0, "open",
// appnamepath.toLocal8Bit().data(),
// nullptr,
// workpath.toLocal8Bit().data(),
// SW_SHOW);
QProcess p;
p.setWorkingDirectory(workpath);
QString command = appnamepath;
p.startDetached(command);
//p.waitForStarted();
qDebug() << QString::fromLocal8Bit("开启进程 %1 ")
.arg(appnamepath)
.toUtf8().data();
}
void CUpdater::runUpdateBat()
{
QString appnamepath = m_strAppPath + QDir::separator() + "update.bat";
QString workpath = QFileInfo(appnamepath).absolutePath();
QProcess p;
p.setWorkingDirectory(workpath);
QString command = appnamepath;
p.start(command);
qDebug() << QString::fromLocal8Bit("运行脚本 %1 ")
.arg(appnamepath)
.toUtf8().data();
p.waitForFinished();
//删除脚本
LibPublic::RemoveFile(appnamepath);
}
bool CUpdater::copyFileWithProcess(const QString& fromfilename, const QString& tofilename)
{
char* bytetemp = new char[4096];//字节数组
int fileSize = 0;
int totalCopySize = 0;
QFile tofile;
emit signalDetailProcessRange(0, 0);
emit signalDetailProcessValue(0);
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件..."));
tofile.setFileName(tofilename);
//覆盖
if (!tofile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
return false;
}
QDataStream out(&tofile);
out.setVersion(QDataStream::Qt_5_12);
QFile fromfile;
fromfile.setFileName(fromfilename);
if (!fromfile.open(QIODevice::ReadOnly))
{
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件失败 文件无法打开 %1").arg(fromfilename));
return false;
}
fileSize = fromfile.size();
QDataStream in(&fromfile);
in.setVersion(QDataStream::Qt_5_12);
emit signalDetailProcessRange(0, fileSize);
emit signalDetailProcessValue(0);
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件...%1/%2").arg(0).arg(fileSize));
while (!in.atEnd())
{
int readSize = 0;
//读入字节数组,返回读取的字节数量,如果小于4096,则到了文件尾
readSize = in.readRawData(bytetemp, 4096);
out.writeRawData(bytetemp, readSize);
totalCopySize += readSize;
emit signalDetailProcessValue(totalCopySize);
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件...%1/%2").arg(totalCopySize).arg(fileSize));
}
if (totalCopySize == fileSize)
{
tofile.setPermissions(QFile::ExeUser);
return true;
}
else
{
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件失败 未成功全部读取文件 %1").arg(fromfilename));
return false;
}
}
bool CUpdater::ExtractsFileWithProcess(const QString& sourcefile, const QString& targetpath)
{
emit signalDetailProcessRange(0, 100);
emit signalDetailProcessValue(0);
emit signalDetailMessage(QString::fromLocal8Bit("解压文件..."));
//目标文件夹
QDir dir(targetpath);
if (dir.exists())
{
//如果存在 清空文件夹
dir.removeRecursively();
}
emit signalDetailProcessValue(50);
int ret = 0;
ret = ExtractsFilesFromArchive(sourcefile, targetpath);
emit signalDetailProcessValue(100);
return ret > -1;
}
bool CUpdater::copyDirWithProcess(const QStringList& lstupdatefile)
{
bool ret = false;
emit signalDetailProcessRange(0, lstupdatefile.count());
emit signalDetailProcessValue(0);
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件夹...%1/%2").arg(0).arg(lstupdatefile.count()));
int i = 0;
for (auto updatefile : lstupdatefile)
{
emit signalDetailProcessValue(i++);
emit signalDetailMessage(QString::fromLocal8Bit("拷贝文件夹...%1/%2").arg(i).arg(lstupdatefile.count()));
QString fromdir = QDir::tempPath() + QDir::separator() + QFileInfo(updatefile).baseName();
QString todir = m_strAppPath;
//复制文件 覆盖
ret = LibPublic::CopyDir(fromdir, todir, true);
qDebug() << QString::fromLocal8Bit("复制文件夹至当前路径 %1 %2")
.arg(fromdir)
.arg(ret)
.toUtf8().data();
if (!ret)
{
return ret;
}
}
return true;
}
void CUpdater::saveVision()
{
//QSettings settings(VISION_CONFIG, QSettings::IniFormat);
//settings.setIniCodec(QTextCodec::codecForLocale());
//settings.setValue("Vision", QString::number(m_fCurVision));
INI(Updater)->setVisionNo(m_strCurVision);
}
void CUpdater::loadVision()
{
//QSettings settings(VISION_CONFIG, QSettings::IniFormat);
//settings.setIniCodec(QTextCodec::codecForLocale());
//m_fCurVision = settings.value("Vision", 0).toFloat();
m_strCurVision = INI(Updater)->getVisionNo();
}
#include <QtWidgets/QApplication>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QTextStream>
#include "uimainwindow.h"
#include "cmylog4qt.h"
#include "cupdater.h"
using namespace std;
//
// 使用说明
// 更新
// RS-Updater.exe -n RS-MonitorUi.exe -p E:\GitWorkSpace\IVO-Code\LCM-Code-AOI\Bin\Release -s D:\UpdateFile
// 判断是否需要更新
// RS-Updater.exe -u
//
//
//
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//日志
//CMyLog4Qt::initLog();
QApplication::setApplicationName("Updater");
QApplication::setApplicationVersion("1.0");
QCommandLineParser cmdparser;
cmdparser.setApplicationDescription(QString::fromLocal8Bit("自动更新命令行帮助说明"));
cmdparser.addHelpOption();
cmdparser.addVersionOption();
//判断是否需要更新
QCommandLineOption isupdateoption(QStringList() << "u" << "IsUpdate", QString::fromLocal8Bit("检查是否需要更新,返回1:需要更新"));
//APP名称
QCommandLineOption appnameoption(QStringList() << "n" << "Name", QString::fromLocal8Bit("程序名称 <AppName>"),"AppName");
//APP路径 默认当前路径
QCommandLineOption apppathoption(QStringList() << "p" << "AppPath", QString::fromLocal8Bit("程序所在路径 <AppPath>"), "AppPath");
//更新服务器地址
QCommandLineOption serverpathoption(QStringList() << "s" << "UpdateServerPath", QString::fromLocal8Bit("更新服务地址 <ServerPath>"), "ServerPath");
//更新包索引 默认Update
QCommandLineOption fileindexoption(QStringList() << "f" << "UpdateFileIndex", QString::fromLocal8Bit("更新包名称索引 <FileIndex>"), "FileIndex");
cmdparser.addOption(isupdateoption);
cmdparser.addOption(appnameoption);
cmdparser.addOption(apppathoption);
cmdparser.addOption(serverpathoption);
cmdparser.addOption(fileindexoption);
cmdparser.process(a);
//获取app名称
QString appname = cmdparser.value(appnameoption);
//获取app路径
QString apppath = cmdparser.value(apppathoption);
//更新服务器路径
QString serverpath = cmdparser.value(serverpathoption);
//更新包名称索引
QString fileindex = cmdparser.value(fileindexoption);
CUpdater::GetKernel()->setAppName(appname);
CUpdater::GetKernel()->setAppPath(apppath);
CUpdater::GetKernel()->setServerPath(serverpath);
CUpdater::GetKernel()->setUpdateFileIndex(fileindex);
//QProcess p;
//p.setWorkingDirectory("C:\\Users\\ysn14\\Desktop\\AOI1_1");
//p.startDetached("C:\\Users\\ysn14\\Desktop\\AOI1_1\\RS-CELLInspectionUi.exe");
//return a.exec();
//判断是否需要更新
if (cmdparser.isSet(isupdateoption))
{
//直接返回值
return CUpdater::GetKernel()->findUpdateFileList();
}
CUpdater::GetKernel()->handleUpdate();
return a.exec();
}