cx_Freeze打包的Python自动更新程序覆盖时触发权限拒绝错误
问题原因及解决方法
问题根源
你遇到的PermissionError: [Errno13] Permission denied,核心原因是正在运行的exe程序及其加载的依赖文件(比如_asyncio.pyd)被Windows系统锁定了。
当cx_Freeze打包的exe运行时,Windows会保持对程序本身和已加载的依赖库(.pyd、.dll这类文件)的打开状态,防止其他进程修改这些正在被使用的文件,所以你在程序运行中试图覆盖这些被占用的文件,就会触发权限拒绝错误。
解决方法
1. 拆分独立更新器(最可靠的方案)
把更新逻辑单独做成一个小型exe程序,流程改成:
- 主程序检查Firebase是否有新版本
- 发现新版本后,启动独立更新器,然后主程序立即退出
- 更新器完成下载、解压覆盖所有文件后,再启动新的主程序
这样更新器运行时,主程序已经完全退出,所有被占用的文件都被释放,就能正常覆盖了。
2. 用临时目录+批处理脚本实现更新(快速实现方案)
不需要额外写更新器,通过临时目录+批处理脚本完成替换:
import sys import subprocess import shutil from pathlib import Path # 原有路径逻辑 this_file_path = sys.executable if getattr(sys, 'frozen', False) else __file__ path_here = str(Path(this_file_path).parent.absolute()) # 下载压缩包(原有代码) storagePath = '/version/File'+ver_recent+'.zip' storage.child(storagePath).download(path='', filename=path_here+'/File.zip') file_name = path_here+'/File.zip' # 解压到临时目录 temp_dir = path_here + "\\temp_update" shutil.unpack_archive(file_name, temp_dir, "zip") # 生成批处理脚本 bat_content = f""" @echo off :: 等待2秒,确保原程序完全退出 timeout /t 2 /nobreak > nul :: 覆盖所有文件 xcopy /E /Y "{temp_dir}\\*" "{path_here}\\" :: 清理临时文件 rmdir /S /Q "{temp_dir}" del "{path_here}\\File.zip" :: 启动新的主程序 start "" "{path_here}\\{Path(this_file_path).name}" """ bat_path = path_here + "\\update.bat" with open(bat_path, "w", encoding="gbk") as f: f.write(bat_content) # 启动批处理,然后退出当前程序 subprocess.Popen([bat_path], shell=True) sys.exit()
注意:
timeout /t 2是给原程序留足够时间退出,避免文件还被占用- 批处理用
gbk编码是为了兼容Windows中文环境
3. 调整cx_Freeze打包方式(辅助优化)
如果用--onefile参数打包成单文件exe,程序运行时会自动解压到系统临时目录,此时你需要更新的是用户实际运行的那个单文件exe(而不是临时目录里的文件),这种场景下也需要让原程序退出,再由其他进程替换exe文件。
关键提醒
Windows系统对正在运行的文件有强制锁定机制,这是系统级限制,不是Python或cx_Freeze的bug。你测试未运行状态能覆盖,正好验证了这一点——只有当文件不被任何进程占用时,才能修改或覆盖。
内容的提问来源于stack exchange,提问作者MooNChilD Song
相关产品推荐
相关产品推荐

