新增windows程序ota逻辑
This commit is contained in:
parent
d049d9d908
commit
15bb1ce07d
@ -20,6 +20,7 @@
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QInputDialog>
|
||||
#include <QStorageInfo>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent), otaOperationPending(false), isUpgradeOperation(true)
|
||||
@ -28,7 +29,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
settings = new QSettings("TuxiApp", "LightStripSN", this);
|
||||
|
||||
// 初始化更新相关变量
|
||||
currentVersion = "1.5.1"; // 当前程序版本
|
||||
currentVersion = "1.5.2"; // 当前程序版本
|
||||
updateServerUrl = settings->value("updateServerUrl", "http://180.163.74.83:8001/version").toString();
|
||||
updateNetworkManager = new QNetworkAccessManager(this);
|
||||
updateCheckReply = nullptr;
|
||||
@ -111,7 +112,7 @@ void MainWindow::showVersionUpdateInfo()
|
||||
*/
|
||||
|
||||
void MainWindow::setupUI() {
|
||||
setWindowTitle("兔喜Test1.5.1 Author:Zhangzhenghao Email:zzh9953477@gmail.com");
|
||||
setWindowTitle("兔喜Test1.5.2 Author:Zhangzhenghao Email:zzh9953477@gmail.com");
|
||||
|
||||
// 参考qt_bak的合理尺寸设置,增加竖向高度
|
||||
setMinimumSize(850, 720); // 增加最小高度
|
||||
@ -1479,9 +1480,10 @@ void MainWindow::showAbout()
|
||||
{
|
||||
QMessageBox::about(this, "关于程序",
|
||||
"兔喜MQTT测试程序\n\n"
|
||||
"版本: 1.2.0\n"
|
||||
"版本: 1.5.2\n"
|
||||
"构建日期: 2025-09-12\n\n"
|
||||
"功能特性:\n"
|
||||
"• 新增灯条备份恢复和程序ota升级功能\n"
|
||||
"• 修复清空全部sn未生效的问题\n"
|
||||
"• 修复label匹配值错误的问题\n"
|
||||
"• 匹配系统颜色。修复浅色模式下字体看不清\n"
|
||||
@ -1541,9 +1543,24 @@ void MainWindow::onUpdateCheckFinished()
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
QString latestVersion = obj["version"].toString();
|
||||
QString downloadUrl = obj["downloadUrl"].toString();
|
||||
QString changelog = obj["changelog"].toString();
|
||||
|
||||
// 根据平台选择合适的下载URL
|
||||
QString downloadUrl;
|
||||
#ifdef Q_OS_WIN
|
||||
downloadUrl = obj["downloadUrlWindows"].toString();
|
||||
if (downloadUrl.isEmpty()) {
|
||||
// 如果没有Windows专用URL,回退到通用URL
|
||||
downloadUrl = obj["downloadUrl"].toString();
|
||||
}
|
||||
#else
|
||||
downloadUrl = obj["downloadUrl"].toString();
|
||||
if (downloadUrl.isEmpty()) {
|
||||
// 如果没有Linux专用URL,尝试Windows URL作为备选
|
||||
downloadUrl = obj["downloadUrlWindows"].toString();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (compareVersions(currentVersion, latestVersion) < 0) {
|
||||
showUpdateDialog(latestVersion, downloadUrl, changelog);
|
||||
} else {
|
||||
@ -1629,16 +1646,45 @@ void MainWindow::onUpdateDownloadFinished()
|
||||
// 保存下载的文件
|
||||
QByteArray data = updateDownloadReply->readAll();
|
||||
QString fileName = QUrl(updateDownloadReply->url()).fileName();
|
||||
|
||||
// 确保文件名不为空
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = "QtDemo.exe"; // 默认文件名
|
||||
}
|
||||
|
||||
downloadedUpdatePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + fileName;
|
||||
|
||||
// 如果文件已存在,先删除
|
||||
if (QFile::exists(downloadedUpdatePath)) {
|
||||
QFile::remove(downloadedUpdatePath);
|
||||
}
|
||||
|
||||
QFile file(downloadedUpdatePath);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
file.write(data);
|
||||
qint64 bytesWritten = file.write(data);
|
||||
file.close();
|
||||
|
||||
// 验证文件是否完整写入
|
||||
if (bytesWritten != data.size()) {
|
||||
QMessageBox::critical(this, "下载失败",
|
||||
QString("文件写入不完整\n期望:%1 字节\n实际:%2 字节")
|
||||
.arg(data.size()).arg(bytesWritten));
|
||||
QFile::remove(downloadedUpdatePath); // 清理不完整的文件
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证下载的文件是否存在且可读
|
||||
QFileInfo fileInfo(downloadedUpdatePath);
|
||||
if (!fileInfo.exists() || !fileInfo.isReadable()) {
|
||||
QMessageBox::critical(this, "下载失败",
|
||||
QString("下载的文件无法访问:%1").arg(downloadedUpdatePath));
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle("下载完成");
|
||||
msgBox.setText("更新包下载完成,是否立即安装?");
|
||||
msgBox.setText(QString("更新包下载完成(%1 MB),是否立即安装?")
|
||||
.arg(QString::number(fileInfo.size() / 1024.0 / 1024.0, 'f', 2)));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
QAbstractButton *yesButton = msgBox.button(QMessageBox::Yes);
|
||||
@ -1650,7 +1696,10 @@ void MainWindow::onUpdateDownloadFinished()
|
||||
installUpdate();
|
||||
}
|
||||
} else {
|
||||
QMessageBox::critical(this, "下载失败", "无法保存更新文件!");
|
||||
QMessageBox::critical(this, "下载失败",
|
||||
QString("无法保存更新文件到:%1\n错误:%2")
|
||||
.arg(downloadedUpdatePath)
|
||||
.arg(file.errorString()));
|
||||
}
|
||||
} else {
|
||||
QMessageBox::warning(this, "下载失败",
|
||||
@ -1681,7 +1730,31 @@ void MainWindow::installUpdate()
|
||||
QString backupPath = currentPath + ".backup";
|
||||
|
||||
// 备份当前程序
|
||||
QFile::copy(currentPath, backupPath);
|
||||
if (!QFile::copy(currentPath, backupPath)) {
|
||||
QString errorDetail = QFile(currentPath).errorString();
|
||||
if (errorDetail.isEmpty()) {
|
||||
errorDetail = "Unknown error";
|
||||
}
|
||||
|
||||
// 在Windows上,可能是因为文件正在使用中
|
||||
#ifdef Q_OS_WIN
|
||||
// 尝试检查文件是否被锁定
|
||||
QFile testFile(currentPath);
|
||||
if (!testFile.open(QIODevice::ReadOnly)) {
|
||||
errorDetail += "\n可能原因:程序文件正在使用中,请确保没有其他实例在运行";
|
||||
} else {
|
||||
testFile.close();
|
||||
// 检查目标目录权限
|
||||
QFileInfo dirInfo(QFileInfo(backupPath).absolutePath());
|
||||
if (!dirInfo.isWritable()) {
|
||||
errorDetail += "\n可能原因:目标目录没有写入权限,请以管理员身份运行";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QMessageBox::warning(this, "安装失败", QString("无法备份当前程序:%1").arg(errorDetail));
|
||||
return;
|
||||
}
|
||||
|
||||
// 替换程序文件
|
||||
QFile::remove(currentPath);
|
||||
@ -1701,14 +1774,207 @@ void MainWindow::installUpdate()
|
||||
QString tempPath = currentPath + ".new";
|
||||
|
||||
// 备份当前程序
|
||||
QFile::copy(currentPath, backupPath);
|
||||
#ifdef Q_OS_WIN
|
||||
// Windows平台:正在运行的exe文件被锁定,需要特殊处理
|
||||
// 先检查备份文件是否已存在,如果存在则删除
|
||||
if (QFile::exists(backupPath)) {
|
||||
if (!QFile::remove(backupPath)) {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("无法删除旧的备份文件:%1\n错误:%2")
|
||||
.arg(backupPath)
|
||||
.arg(QFile(backupPath).errorString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 在Windows上,尝试使用系统命令复制正在运行的文件
|
||||
QProcess copyProcess;
|
||||
QString copyCmd = QString("copy /Y \"%1\" \"%2\"")
|
||||
.arg(QDir::toNativeSeparators(currentPath))
|
||||
.arg(QDir::toNativeSeparators(backupPath));
|
||||
|
||||
copyProcess.start("cmd.exe", QStringList() << "/c" << copyCmd);
|
||||
copyProcess.waitForFinished(5000);
|
||||
|
||||
if (copyProcess.exitCode() != 0 || !QFile::exists(backupPath)) {
|
||||
// 如果系统命令也失败,尝试使用Qt的方式,但先尝试解锁文件
|
||||
QFile currentFile(currentPath);
|
||||
if (!currentFile.copy(backupPath)) {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("无法备份当前程序\n当前程序:%1\n备份位置:%2\n\n这可能是因为程序正在运行被系统锁定。\n建议:\n1. 关闭所有程序实例\n2. 以管理员身份运行\n3. 或手动复制程序文件进行备份")
|
||||
.arg(currentPath)
|
||||
.arg(backupPath));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Linux/Unix平台的备份逻辑
|
||||
// 先检查备份文件是否已存在,如果存在则删除
|
||||
if (QFile::exists(backupPath)) {
|
||||
if (!QFile::remove(backupPath)) {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("无法删除旧的备份文件:%1\n错误:%2")
|
||||
.arg(backupPath)
|
||||
.arg(QFile(backupPath).errorString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::copy(currentPath, backupPath)) {
|
||||
QString errorDetail = QFile(currentPath).errorString();
|
||||
if (errorDetail.isEmpty()) {
|
||||
errorDetail = "Unknown error";
|
||||
}
|
||||
|
||||
// 添加Linux平台的详细诊断信息
|
||||
QFileInfo currentFileInfo(currentPath);
|
||||
QFileInfo backupDirInfo(QFileInfo(backupPath).absolutePath());
|
||||
|
||||
QString diagnosticInfo = QString("\n\n诊断信息:\n");
|
||||
diagnosticInfo += QString("- 当前程序路径:%1\n").arg(currentPath);
|
||||
diagnosticInfo += QString("- 备份路径:%1\n").arg(backupPath);
|
||||
diagnosticInfo += QString("- 当前程序是否存在:%1\n").arg(currentFileInfo.exists() ? "是" : "否");
|
||||
diagnosticInfo += QString("- 当前程序是否可读:%1\n").arg(currentFileInfo.isReadable() ? "是" : "否");
|
||||
diagnosticInfo += QString("- 备份目录是否可写:%1\n").arg(backupDirInfo.isWritable() ? "是" : "否");
|
||||
diagnosticInfo += QString("- 当前程序大小:%1 字节\n").arg(currentFileInfo.size());
|
||||
|
||||
// 尝试更详细的错误检查
|
||||
QFile sourceFile(currentPath);
|
||||
QFile targetFile(backupPath);
|
||||
|
||||
// 检查源文件是否可以打开
|
||||
if (!sourceFile.open(QIODevice::ReadOnly)) {
|
||||
diagnosticInfo += QString("- 源文件打开失败:%1\n").arg(sourceFile.errorString());
|
||||
} else {
|
||||
sourceFile.close();
|
||||
diagnosticInfo += "- 源文件可以正常打开\n";
|
||||
}
|
||||
|
||||
// 检查目标文件是否可以创建
|
||||
if (!targetFile.open(QIODevice::WriteOnly)) {
|
||||
diagnosticInfo += QString("- 目标文件创建失败:%1\n").arg(targetFile.errorString());
|
||||
} else {
|
||||
targetFile.close();
|
||||
targetFile.remove(); // 清理测试文件
|
||||
diagnosticInfo += "- 目标文件可以正常创建\n";
|
||||
}
|
||||
|
||||
// 检查是否有权限问题
|
||||
if (!backupDirInfo.isWritable()) {
|
||||
diagnosticInfo += "\n可能的解决方案:\n";
|
||||
diagnosticInfo += "1. 检查目录写入权限\n";
|
||||
diagnosticInfo += "2. 尝试以sudo权限运行程序\n";
|
||||
diagnosticInfo += "3. 更改程序安装目录的权限";
|
||||
}
|
||||
|
||||
// 检查磁盘空间
|
||||
QStorageInfo storage(backupDirInfo.absolutePath());
|
||||
if (storage.isValid()) {
|
||||
qint64 availableBytes = storage.bytesAvailable();
|
||||
qint64 requiredBytes = currentFileInfo.size();
|
||||
diagnosticInfo += QString("- 可用磁盘空间:%1 字节\n").arg(availableBytes);
|
||||
if (availableBytes < requiredBytes) {
|
||||
diagnosticInfo += "\n错误:磁盘空间不足!";
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试使用系统命令作为备选方案
|
||||
diagnosticInfo += "\n尝试使用系统命令备份...\n";
|
||||
QProcess cpProcess;
|
||||
QString cpCmd = QString("cp \"%1\" \"%2\"").arg(currentPath).arg(backupPath);
|
||||
cpProcess.start("sh", QStringList() << "-c" << cpCmd);
|
||||
cpProcess.waitForFinished(5000);
|
||||
|
||||
if (cpProcess.exitCode() == 0 && QFile::exists(backupPath)) {
|
||||
diagnosticInfo += "系统命令备份成功!继续安装过程...\n";
|
||||
// 如果系统命令成功,继续执行后续逻辑
|
||||
} else {
|
||||
diagnosticInfo += QString("系统命令也失败了,退出码:%1\n").arg(cpProcess.exitCode());
|
||||
diagnosticInfo += QString("系统命令错误输出:%1\n").arg(QString::fromLocal8Bit(cpProcess.readAllStandardError()));
|
||||
|
||||
QMessageBox::warning(this, "安装失败", QString("无法备份当前程序:%1%2").arg(errorDetail).arg(diagnosticInfo));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// 先将新文件复制到临时位置
|
||||
// 确保临时文件不存在
|
||||
if (QFile::exists(tempPath)) {
|
||||
if (!QFile::remove(tempPath)) {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("无法删除旧的临时文件:%1").arg(tempPath));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::copy(downloadedUpdatePath, tempPath)) {
|
||||
QMessageBox::warning(this, "安装失败", "无法复制更新文件!");
|
||||
QString errorMsg = QString("无法复制更新文件到临时位置\n源文件:%1\n目标文件:%2\n错误:%3")
|
||||
.arg(downloadedUpdatePath)
|
||||
.arg(tempPath)
|
||||
.arg(QFile(downloadedUpdatePath).errorString());
|
||||
QMessageBox::warning(this, "安装失败", errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证复制的文件
|
||||
QFileInfo sourceInfo(downloadedUpdatePath);
|
||||
QFileInfo tempInfo(tempPath);
|
||||
if (!tempInfo.exists() || tempInfo.size() != sourceInfo.size()) {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("复制的临时文件不完整\n源文件大小:%1 字节\n临时文件大小:%2 字节")
|
||||
.arg(sourceInfo.size())
|
||||
.arg(tempInfo.exists() ? tempInfo.size() : 0));
|
||||
QFile::remove(tempPath); // 清理不完整的文件
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// Windows平台的更新和重启逻辑
|
||||
// 在Windows上,检查目标目录是否可写
|
||||
QFileInfo dirInfo(currentDir);
|
||||
if (!dirInfo.isWritable()) {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("目标目录没有写入权限:%1\n请以管理员身份运行程序或选择其他安装位置。")
|
||||
.arg(currentDir));
|
||||
QFile::remove(tempPath); // 清理临时文件
|
||||
return;
|
||||
}
|
||||
|
||||
QString scriptPath = currentDir + "/update_and_restart.bat";
|
||||
QFile scriptFile(scriptPath);
|
||||
if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QTextStream out(&scriptFile);
|
||||
out << "@echo off\r\n";
|
||||
out << "timeout /t 2 /nobreak >nul\r\n"; // 等待当前进程完全退出
|
||||
out << "REM 移除旧文件并重命名新文件\r\n";
|
||||
out << "del /f /q \"" << QDir::toNativeSeparators(currentPath) << "\"\r\n";
|
||||
out << "move \"" << QDir::toNativeSeparators(tempPath) << "\" \"" << QDir::toNativeSeparators(currentPath) << "\"\r\n";
|
||||
out << "cd /d \"" << QDir::toNativeSeparators(currentDir) << "\"\r\n";
|
||||
out << "REM 启动新程序\r\n";
|
||||
out << "start \"\" \"" << QDir::toNativeSeparators(currentFileName) << "\"\r\n";
|
||||
out << "REM 清理脚本文件\r\n";
|
||||
out << "timeout /t 1 /nobreak >nul\r\n";
|
||||
out << "del /f /q \"" << QDir::toNativeSeparators(scriptPath) << "\"\r\n";
|
||||
scriptFile.close();
|
||||
|
||||
// 启动更新脚本并退出当前程序
|
||||
QProcess::startDetached("cmd.exe", QStringList() << "/c" << QDir::toNativeSeparators(scriptPath));
|
||||
QMessageBox::information(this, "安装完成", "更新安装完成,程序将自动重启!");
|
||||
|
||||
// 延迟退出,确保脚本有时间启动
|
||||
QTimer::singleShot(500, []() {
|
||||
QApplication::quit();
|
||||
});
|
||||
} else {
|
||||
QMessageBox::warning(this, "安装失败",
|
||||
QString("无法创建更新脚本:%1\n错误:%2")
|
||||
.arg(scriptPath)
|
||||
.arg(scriptFile.errorString()));
|
||||
QFile::remove(tempPath); // 清理临时文件
|
||||
}
|
||||
#else
|
||||
// Linux/Unix平台的更新和重启逻辑
|
||||
// 设置临时文件权限
|
||||
QFile::setPermissions(tempPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner |
|
||||
QFile::ReadGroup | QFile::ExeGroup |
|
||||
@ -1750,6 +2016,7 @@ void MainWindow::installUpdate()
|
||||
QMessageBox::warning(this, "安装失败", "无法创建更新脚本!");
|
||||
QFile::remove(tempPath); // 清理临时文件
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user