PyInstaller打包Python exe实现关闭前执行退出逻辑的问题
解决方案
在Windows系统中,点击控制台窗口关闭按钮触发的是CTRL_CLOSE_EVENT,这属于Windows特有的系统事件,常规的atexit和Unix风格的信号监听(如SIGINT、SIGTERM)无法捕获该事件。需要通过调用Win32 API注册控制台事件处理函数来实现需求。
具体实现步骤
- 使用
ctypes调用Windows内核的SetConsoleCtrlHandler函数,注册自定义的事件处理回调。 - 在回调函数中处理控制台关闭事件,执行你的清理逻辑,同时确保服务(如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
相关产品推荐
相关产品推荐

