diff --git a/bin/stop-emrg.cmd b/bin/stop-emrg.cmd index 43bd337..4b0849a 100644 --- a/bin/stop-emrg.cmd +++ b/bin/stop-emrg.cmd @@ -5,8 +5,9 @@ REM stuck at "停止已有进程" because the windowless pythonw daemon holds fi REM and Inno CloseApplications cannot see it). REM REM Order matters (mirrors bin/emrg-uninstall steps 1a/1b): -REM 1. GUI (EMRG.exe): graceful WM_CLOSE first (taskkill without /F), -REM /F fallback after ~5s if still alive +REM 1. GUI (EMRG.exe): graceful WM_CLOSE first (taskkill without /F), then +REM UNCONDITIONAL /F after the ~5s grace window (host 01:27:07Z: long-lived +REM GUI deferred WM_CLOSE past 5s — /F must not be gated on a survivor check) REM 2. TUI (python.exe -m emrg): command-line filter (wmic, PowerShell REM fallback), excludes the daemon (pythonw.exe -m emrg.server) REM 3. daemon: `emrg server stop` protocol shutdown via the OLD install's CLI @@ -20,14 +21,19 @@ setlocal enabledelayedexpansion set "EMRG_DIR=%USERPROFILE%\.emrg" set "INSTALL=%EMRG_DIR%\install" -REM --- 1. GUI: graceful WM_CLOSE, then /F fallback --- +REM --- 1. GUI: graceful WM_CLOSE, then unconditional /F after the grace window --- +REM (host report 2026-08-10T01:27:07Z: a long-lived ~15h GUI session deferred +REM WM_CLOSE past the 5s window — two full script runs did not terminate it +REM while a direct `taskkill /IM EMRG.exe` did. => the /F fallback must be +REM UNCONDITIONAL after the wait so an old GUI session can never hold the +REM installer hostage. When the GUI is not running, the graceful taskkill +REM returns errorlevel 1 (skip the wait) and the /F below is a fast no-op.) taskkill /IM EMRG.exe >nul 2>&1 if not errorlevel 1 ( REM give the GUI up to ~5s to exit cleanly (ping = portable sleep) ping -n 6 127.0.0.1 >nul 2>&1 ) -tasklist /FI "IMAGENAME eq EMRG.exe" 2>nul | findstr /i "EMRG.exe" >nul -if not errorlevel 1 taskkill /F /IM EMRG.exe >nul 2>&1 +taskkill /F /IM EMRG.exe >nul 2>&1 REM --- 2. TUI: python.exe -m emrg (daemon is pythonw.exe -m emrg.server, excluded) --- where wmic >nul 2>&1 diff --git a/tests/test_installer_stop.py b/tests/test_installer_stop.py index 5a54764..4b79462 100644 --- a/tests/test_installer_stop.py +++ b/tests/test_installer_stop.py @@ -22,6 +22,14 @@ def test_stop_emrg_cmd_covers_gui_tui_daemon_in_order(): assert "taskkill /IM EMRG.exe" in content assert "taskkill /F /IM EMRG.exe" in content assert content.index("taskkill /IM EMRG.exe") < content.index("taskkill /F /IM EMRG.exe") + # 宿主 2026-08-10T01:27:07Z 实测:长活 ~15h GUI 会话 WM_CLOSE 在 5s 窗内未退出, + # 两次整跑未终止、直接 taskkill /F 才终止 → /F 必须是无条件兜底(不 gate 在 + # survivor 检查上),否则旧 GUI 会话可无限期拖住安装器。判别:ping 等待之后、 + # /F 之前不得再有 findstr 判定。 + ping_idx = content.index("ping -n 6 127.0.0.1") + f_idx = content.index("taskkill /F /IM EMRG.exe") + assert ping_idx < f_idx + assert "findstr" not in content[ping_idx:f_idx] # TUI:命令行过滤(wmic LIKE 通配符须 %% 转义) assert "wmic" in content and "commandline like" in content assert "powershell" in content # wmic 缺失(Win11 24H2+)时的回退