[patch] 新增自动更新功能 模块 优化进度条显示 横幅图片资源上传
This commit is contained in:
135
auto_updater.py
135
auto_updater.py
@@ -14,6 +14,7 @@ import time
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
import ctypes
|
||||
|
||||
|
||||
class UpdateDownloader(QThread):
|
||||
@@ -131,6 +132,59 @@ class UpdateDownloader(QThread):
|
||||
self.is_cancelled = True
|
||||
|
||||
|
||||
def run_as_admin(script_path):
|
||||
"""
|
||||
以管理员身份运行脚本(Windows UAC提升)
|
||||
|
||||
特点:
|
||||
- 在GUI退出前弹出UAC提示
|
||||
- 用户点击"允许"后,脚本以管理员身份运行
|
||||
- 用户点击"取消",返回False
|
||||
|
||||
Args:
|
||||
script_path: 批处理脚本路径
|
||||
|
||||
Returns:
|
||||
bool: True表示成功启动(用户点击允许),False表示失败或用户取消
|
||||
"""
|
||||
try:
|
||||
print(f"[RunAsAdmin] 请求管理员权限执行: {script_path}")
|
||||
|
||||
# 使用 ShellExecute 以管理员身份运行
|
||||
# "runas" 参数会触发UAC提示
|
||||
ret = ctypes.windll.shell32.ShellExecuteW(
|
||||
None, # hwnd: 父窗口句柄(None表示无父窗口)
|
||||
"runas", # lpOperation: 以管理员身份运行
|
||||
"cmd.exe", # lpFile: 要执行的程序
|
||||
f'/c "{script_path}"', # lpParameters: 命令行参数
|
||||
None, # lpDirectory: 工作目录(None表示当前目录)
|
||||
0 # nShowCmd: 0=隐藏窗口
|
||||
)
|
||||
|
||||
# 返回值说明:
|
||||
# >32: 成功
|
||||
# <=32: 失败(具体错误码)
|
||||
# - 5: 用户拒绝UAC(点击"否")
|
||||
# - 2: 文件未找到
|
||||
# - 其他: 其他错误
|
||||
|
||||
if ret > 32:
|
||||
print(f"[RunAsAdmin] ✅ 成功:用户已授权,脚本正在以管理员身份运行")
|
||||
return True
|
||||
elif ret == 5:
|
||||
print(f"[RunAsAdmin] ⚠️ 用户取消了UAC授权(返回码: {ret})")
|
||||
return False
|
||||
else:
|
||||
print(f"[RunAsAdmin] ❌ 启动失败(返回码: {ret})")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"[RunAsAdmin] ❌ 异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def install_update_and_restart(installer_path):
|
||||
"""
|
||||
简化版更新安装(文件替换方式,不使用NSIS安装)
|
||||
@@ -138,9 +192,10 @@ def install_update_and_restart(installer_path):
|
||||
工作流程:
|
||||
1. 将安装包移动到程序目录
|
||||
2. 创建替换脚本
|
||||
3. 脚本等待程序退出
|
||||
4. 脚本解压并替换文件
|
||||
5. 重启程序
|
||||
3. 以管理员身份启动脚本(提前弹出UAC)
|
||||
4. 脚本等待程序退出
|
||||
5. 脚本执行静默安装
|
||||
6. 重启程序
|
||||
|
||||
Args:
|
||||
installer_path: 安装包路径
|
||||
@@ -187,16 +242,16 @@ def install_update_and_restart(installer_path):
|
||||
exe_name
|
||||
)
|
||||
|
||||
# 启动更新脚本(独立进程)
|
||||
print(f"[Updater] 启动更新脚本: {update_script_path}")
|
||||
subprocess.Popen(
|
||||
['cmd', '/c', update_script_path],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS,
|
||||
close_fds=True
|
||||
)
|
||||
# 🔥 关键修复:以管理员身份启动批处理脚本,提前触发UAC
|
||||
print(f"[Updater] 以管理员身份启动更新脚本: {update_script_path}")
|
||||
success = run_as_admin(update_script_path)
|
||||
|
||||
print(f"[Updater] ✅ 更新脚本已启动")
|
||||
return True
|
||||
if success:
|
||||
print(f"[Updater] ✅ 更新脚本已以管理员身份启动")
|
||||
return True
|
||||
else:
|
||||
print(f"[Updater] ❌ 用户取消了UAC授权,更新已取消")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Updater] ❌ 启动更新失败: {e}")
|
||||
@@ -231,65 +286,67 @@ def create_update_installer_script(installer_path, install_dir, exe_name):
|
||||
install_dir = os.path.abspath(install_dir)
|
||||
|
||||
script_content = f"""@echo off
|
||||
chcp 65001 > nul
|
||||
title 水滴AI客服智能助手 - 自动更新
|
||||
REM ShuiDi AI Assistant Auto Update Script
|
||||
REM Encoding: UTF-8 BOM
|
||||
|
||||
title ShuiDi AI Assistant - Auto Update
|
||||
|
||||
echo ============================================
|
||||
echo 水滴AI客服智能助手 - 自动更新
|
||||
echo ShuiDi AI Assistant Auto Update
|
||||
echo ============================================
|
||||
echo.
|
||||
|
||||
REM 切换到程序目录
|
||||
REM Change to program directory
|
||||
cd /d "{install_dir}"
|
||||
echo [INFO] 程序目录: {install_dir}
|
||||
echo INFO: Program directory: {install_dir}
|
||||
echo.
|
||||
|
||||
REM 等待主程序退出(最多等待30秒)
|
||||
echo [INFO] 等待主程序退出...
|
||||
REM Wait for main program to exit (max 60 seconds)
|
||||
echo INFO: Waiting for main program to exit...
|
||||
set count=0
|
||||
:wait_main_exit
|
||||
tasklist /FI "IMAGENAME eq {exe_name}" 2>NUL | find /I "{exe_name}" >NUL
|
||||
if NOT ERRORLEVEL 1 (
|
||||
if %count% LSS 30 (
|
||||
if %count% LSS 60 (
|
||||
timeout /t 1 /nobreak > nul
|
||||
set /a count+=1
|
||||
goto wait_main_exit
|
||||
) else (
|
||||
echo [WARN] 主程序未正常退出,强制继续安装
|
||||
echo WARN: Main program did not exit normally, forcing installation
|
||||
)
|
||||
)
|
||||
echo [OK] 主程序已退出
|
||||
echo OK: Main program exited
|
||||
echo.
|
||||
|
||||
REM 执行静默安装(在当前目录下)
|
||||
echo [INFO] 开始安装更新...
|
||||
echo [INFO] 安装包: "{installer_path}"
|
||||
echo [INFO] 目标目录: "{install_dir}"
|
||||
REM Execute NSIS silent installation
|
||||
echo INFO: Starting installation...
|
||||
echo INFO: Installer: "{installer_path}"
|
||||
echo INFO: Target directory: "{install_dir}"
|
||||
echo.
|
||||
|
||||
REM 使用完整路径和引号避免空格问题
|
||||
"{installer_path}" /S /D="{install_dir}"
|
||||
REM Execute installation (with admin rights from NSIS)
|
||||
"{installer_path}" /S /D={install_dir}
|
||||
|
||||
REM 等待安装完成
|
||||
echo [INFO] 等待安装完成...
|
||||
timeout /t 20 /nobreak > nul
|
||||
REM Wait for installation to complete
|
||||
echo INFO: Waiting for installation to complete...
|
||||
timeout /t 25 /nobreak > nul
|
||||
|
||||
REM 清理安装包
|
||||
echo [INFO] 清理安装包...
|
||||
REM Cleanup installer
|
||||
echo INFO: Cleaning up installer...
|
||||
del /f /q "{installer_path}" 2>nul
|
||||
|
||||
REM 启动新版本程序
|
||||
echo [INFO] 正在启动新版本...
|
||||
REM Start new version
|
||||
echo INFO: Starting new version...
|
||||
if exist "{install_dir}\\{exe_name}" (
|
||||
start "" "{install_dir}\\{exe_name}"
|
||||
echo [OK] ✅ 程序已启动
|
||||
echo OK: Program started successfully
|
||||
) else (
|
||||
echo [ERROR] ❌ 找不到程序文件: {exe_name}
|
||||
echo [INFO] 请手动启动程序
|
||||
echo ERROR: Program file not found: {exe_name}
|
||||
echo INFO: Please start program manually
|
||||
pause
|
||||
)
|
||||
|
||||
REM 延迟后删除自己
|
||||
REM Delete this script after delay
|
||||
timeout /t 2 /nobreak > nul
|
||||
del /f /q "%~f0" 2>nul
|
||||
exit
|
||||
|
||||
Reference in New Issue
Block a user