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

PyInstaller打包Python exe实现关闭前执行退出逻辑的问题

解决方案

在Windows系统中,点击控制台窗口关闭按钮触发的是CTRL_CLOSE_EVENT,这属于Windows特有的系统事件,常规的atexit和Unix风格的信号监听(如SIGINT、SIGTERM)无法捕获该事件。需要通过调用Win32 API注册控制台事件处理函数来实现需求。

具体实现步骤

  1. 使用ctypes调用Windows内核的SetConsoleCtrlHandler函数,注册自定义的事件处理回调。
  2. 在回调函数中处理控制台关闭事件,执行你的清理逻辑,同时确保服务(如uvicorn)能正常终止。

完整代码示例

import sys
import ctypes
from ctypes import wintypes
import uvicorn
from fastapi import FastAPI

app = FastAPI()

# 定义Windows API所需的类型
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
BOOL = wintypes.BOOL
DWORD = wintypes.DWORD

# 控制台事件类型定义
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6

# 定义回调函数的类型
HandlerRoutine = ctypes.WINFUNCTYPE(BOOL, DWORD)

server = None

def console_ctrl_handler(dwCtrlType):
    if dwCtrlType == CTRL_CLOSE_EVENT:
        # 执行你需要的终止前代码
        print("I want this to print")
        
        # 关闭uvicorn服务器(如果需要)
        if server:
            server.should_exit = True
        
        # 返回True表示已处理该事件,系统不会强制立即终止
        return True
    return False

def main():
    global server
    # 注册控制台事件处理函数
    handler = HandlerRoutine(console_ctrl_handler)
    if not kernel32.SetConsoleCtrlHandler(handler, True):
        raise ctypes.WinError(ctypes.get_last_error())
    
    # 启动uvicorn(采用非阻塞方式,便于处理关闭事件)
    config = uvicorn.Config(app, host="0.0.0.0", port=8000)
    server = uvicorn.Server(config)
    server.run()

if __name__ == "__main__":
    main()

关键说明

  • SetConsoleCtrlHandler:向Windows系统注册回调函数,当控制台收到关闭、注销等事件时会触发该回调。
  • CTRL_CLOSE_EVENT:对应点击控制台窗口右上角关闭按钮的事件类型。
  • 回调函数返回True:告诉Windows系统我们已处理该事件,允许程序执行完清理逻辑后再退出,而非强制终止。
  • 非阻塞启动uvicorn:通过uvicorn.Server的方式启动,便于在回调中设置should_exit来优雅关闭服务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:03:31