You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyInstaller生成exe自动更新遇WinError32文件占用问题求助

解决PyInstaller生成EXE更新时文件被占用的问题

出现WinError 32的核心原因是:主进程file1.exe还在运行(或未完全释放文件句柄)时,updateloader.exe尝试覆盖它——Windows系统不允许修改正在被进程占用的可执行文件。下面是几种可行的解决方法:

方法1:让更新器等待主进程完全退出

主进程启动更新器时,把自身PID传递给更新器,更新器等待该PID对应的进程彻底结束后,再执行复制操作。这种方法最可靠,无需依赖不确定的延迟时间。

主进程(file1.exe)代码修改:

import atexit
import subprocess
import os

def launch_updater():
    updater_path = os.path.join(os.path.dirname(__file__), "updateloader.exe")
    # 将当前进程PID作为参数传给更新器
    subprocess.Popen([updater_path, str(os.getpid())])

# 注册退出时启动更新器
atexit.register(launch_updater)

更新器(updateloader.exe)代码:

如果不想额外依赖第三方库,用Windows原生API实现进程等待:

import sys
import ctypes
from ctypes import wintypes
import shutil

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
SYNCHRONIZE = 0x00100000
INFINITE = -1

# 定义Windows API参数类型
kernel32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
kernel32.OpenProcess.restype = wintypes.HANDLE
kernel32.WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD]
kernel32.WaitForSingleObject.restype = wintypes.DWORD
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]

def wait_for_process_exit(pid):
    # 打开目标进程获取句柄
    h_process = kernel32.OpenProcess(SYNCHRONIZE, False, pid)
    if not h_process:
        return
    # 等待进程退出
    kernel32.WaitForSingleObject(h_process, INFINITE)
    # 关闭句柄
    kernel32.CloseHandle(h_process)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.exit(1)
    
    parent_pid = int(sys.argv[1])
    wait_for_process_exit(parent_pid)

    # 执行更新操作
    src = "C:\\Users\\xy\\Downloads\\file1.exe"
    dst = "C:\\Users\\xy\\folder\\file1.exe"
    try:
        shutil.copy2(src, dst)
        print("更新完成")
    except Exception as e:
        print(f"更新失败: {str(e)}")

如果允许使用第三方库,用psutil会更简洁:

import sys
import psutil
import shutil
import time

def wait_for_process_exit(pid):
    try:
        proc = psutil.Process(pid)
        while proc.is_running():
            time.sleep(0.5)
    except psutil.NoSuchProcess:
        pass

if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.exit(1)
    
    parent_pid = int(sys.argv[1])
    wait_for_process_exit(parent_pid)

    src = "C:\\Users\\xy\\Downloads\\file1.exe"
    dst = "C:\\Users\\xy\\folder\\file1.exe"
    try:
        shutil.copy2(src, dst)
        print("更新完成")
    except Exception as e:
        print(f"更新失败: {str(e)}")

打包updateloader.exe时,记得把psutil包含进去(如果用这个版本)。

方法2:重命名旧文件再替换

Windows允许重命名正在运行的可执行文件,我们可以先把旧的file1.exe重命名为临时文件,再复制新文件到目标路径,下次启动新程序时删除临时文件。

更新器(updateloader.exe)代码:

import os
import shutil
import sys

def main():
    src = "C:\\Users\\xy\\Downloads\\file1.exe"
    dst = "C:\\Users\\xy\\folder\\file1.exe"
    old_file_path = dst + ".old"

    # 重命名旧文件
    if os.path.exists(dst):
        try:
            os.rename(dst, old_file_path)
        except Exception as e:
            print(f"无法重命名旧文件: {str(e)}")
            sys.exit(1)
    
    # 复制新文件
    try:
        shutil.copy2(src, dst)
        print("更新完成")
    except Exception as e:
        # 复制失败,恢复旧文件
        if os.path.exists(old_file_path):
            os.rename(old_file_path, dst)
        print(f"更新失败: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()

主进程(file1.exe)启动时清理旧文件:

在file1.exe的初始化代码中加入:

import os

def cleanup_old_version():
    old_file_path = os.path.join(os.path.dirname(__file__), "file1.exe.old")
    if os.path.exists(old_file_path):
        try:
            os.remove(old_file_path)
        except:
            # 清理失败不影响主程序运行
            pass

# 启动时执行清理
cleanup_old_version()

方法3:主程序主动退出后启动更新器

如果用户确认更新,主程序先启动更新器,然后立即退出,更新器短暂等待后执行替换。这种方法简单但依赖等待时间,可能存在不确定性。

主进程(file1.exe)代码:

import subprocess
import sys
import os
import tkinter.messagebox as msgbox

def check_and_update():
    # 这里是你的版本检查逻辑,假设已确认需要更新
    if msgbox.askyesno("更新提示", "发现新版本,是否更新?"):
        updater_path = os.path.join(os.path.dirname(__file__), "updateloader.exe")
        subprocess.Popen([updater_path])
        # 立即退出主程序
        sys.exit(0)

更新器(updateloader.exe)代码:

import time
import shutil

# 等待3秒,确保主进程完全退出
time.sleep(3)

src = "C:\\Users\\xy\\Downloads\\file1.exe"
dst = "C:\\Users\\xy\\folder\\file1.exe"
try:
    shutil.copy2(src, dst)
    print("更新完成")
except Exception as e:
    print(f"更新失败: {str(e)}")

内容的提问来源于stack exchange,提问作者Fab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:04:56