[BUG] 开启"启动后关闭启动器"时任务栏图标滞留 — 原因已定位(kill_pyappify 硬杀进程), 附修复补丁
Author: sakurairsoraCreated Sep 22, 2026Updated Sep 22, 2026
Labelsbugarea: accountpriority: normal
问题
开启设置里的 "启动后关闭启动器" (Kill Launcher After Start, 默认开启) 后, 启动器进程被强杀, 任务栏/托盘会残留无法点击的幽灵图标, 鼠标悬停后才消失。同 #1405, 该 issue 无回复, 这里重新整理原因和修复方案。
根因
MainWindow 的对应分支调用 pyappify.kill_pyappify(), 其实现是:
os.kill(pid, signal.SIGTERM)在 Windows 上这等价于 TerminateProcess 硬杀: Tauri 启动器进程瞬间消失, 没有机会走自己的退出清理流程, 托盘注册的图标成了幽灵图标; 窗口按钮 + 托盘图标都没被正常注销, 所以会同时看到两个残留。
修复(已在本地实测通过)
杀之前先温和关闭: 先向启动器可见窗口发 WM_CLOSE; 3 秒未退出 (Tauri 拦截了关闭事件时), 再向它的窗口消息线程投 WM_QUIT, 让 tao/Tauri 事件循环正常退出并执行清理 (注销托盘图标); 都失败才回退到原来的 SIGTERM。
def _graceful_close_launcher(process_pid, exit_event=None):
"""Close the launcher gracefully: WM_CLOSE first, then WM_QUIT to its window
threads, so the Tauri app exits through its normal shutdown path and
unregisters its tray icon (a hard kill leaves a ghost tray icon)."""
import ctypes
user32 = ctypes.windll.user32
WM_CLOSE = 0x0010
WM_QUIT = 0x0012
windows = []
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_void_p)
def _callback(hwnd, _lparam):
owner_pid = ctypes.c_ulong()
tid = user32.GetWindowThreadProcessId(hwnd, ctypes.byref(owner_pid))
if owner_pid.value == process_pid:
windows.append((hwnd, tid, bool(user32.IsWindowVisible(hwnd))))
return True
user32.EnumWindows(EnumWindowsProc(_callback), 0)
if not windows:
return False
for hwnd, _tid, visible in windows:
if visible:
user32.PostMessageW(hwnd, WM_CLOSE, 0, 0)
if _wait_for_process_exit(process_pid, 3, exit_event=exit_event):
return True
for tid in {tid for _hwnd, tid, _visible in windows}:
user32.PostThreadMessageW(tid, WM_QUIT, 0, 0)
return _wait_for_process_exit(process_pid, 7, exit_event=exit_event)
def kill_pyappify(timeout=30, exit_event=None):
if pid:
log = _get_logger()
log.info(f"Attempting to terminate process with PID: {pid}")
try:
if _graceful_close_launcher(pid, exit_event=exit_event):
log.info(f'graceful close success {pid}')
return True
log.warning(f'graceful close did not exit pid {pid}, falling back to terminate')
except Exception as e:
log.warning(f'graceful close failed: {e}')
try:
os.kill(pid, signal.SIGTERM)
if not _wait_for_process_exit(pid, timeout, exit_event=exit_event):
log.warning(f"Timed out waiting for process with PID {pid} to exit.")
return False
log.info(f'_wait_for_process_exit success {pid}')
return True
except Exception as e:
log.error(f"Failed to terminate process with PID {pid}: {e}")
return False
return False实测
- 日志:
graceful close success— WM_QUIT 后启动器约 3 秒内正常退出 - 任务栏/托盘经 UI Automation 全量扫描, 0 个 ok-ww 残留按钮
- 硬杀兜底逻辑保留, 温和关闭失败时行为与现在一致
备注
master 分支已改为依赖 pip 包 pyappify==1.0.13 (应用仓库里不再内置这个模块), 所以这个修复需要落到 pyappify 包的源码里再发版; PyPI 上的 wheel 没有附源码仓库链接, 只能麻烦 ok-oldking 在包的源码处套用上面的改动。在包更新之前, v3.6.x 用户可以直接按补丁修改 working/pyappify/__init__.py。
(本 issue 整合自 #1405, 那边的内容已合并到这里。)
Source: ok-oldking/ok-wuthering-waves