From 9703f361f94ba740c9c2c6223bb7f2f0d27419ca Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:51:44 +0800 Subject: [PATCH] fix: add -WindowStyle Hidden to powershell command to prevent window flash on Windows (#285) --- electron/gateway/manager.ts | 10 ++++-- scripts/installer.nsh | 67 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/electron/gateway/manager.ts b/electron/gateway/manager.ts index eec06d333..b1f9a57a6 100644 --- a/electron/gateway/manager.ts +++ b/electron/gateway/manager.ts @@ -722,8 +722,10 @@ export class GatewayManager extends EventEmitter { try { // Platform-specific command to find processes listening on the gateway port. // On Windows, lsof doesn't exist; use PowerShell's Get-NetTCPConnection instead. + // -WindowStyle Hidden is used to prevent PowerShell from popping up a brief console window + // even when windowsHide: true is passed to cp.exec. const cmd = process.platform === 'win32' - ? `powershell -NoProfile -Command "(Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue).OwningProcess"` + ? `powershell -WindowStyle Hidden -NoProfile -Command "(Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue).OwningProcess"` : `lsof -i :${port} -sTCP:LISTEN -t`; const { stdout } = await new Promise<{ stdout: string }>((resolve, reject) => { @@ -754,10 +756,12 @@ export class GatewayManager extends EventEmitter { for (const pid of pids) { try { if (process.platform === 'win32') { - // On Windows, use taskkill for reliable process group termination + // Use PowerShell with -WindowStyle Hidden to kill the process without + // flashing a black console window. taskkill.exe is a console app and + // can flash a window even when windowsHide: true is set. import('child_process').then(cp => { cp.exec( - `taskkill /PID ${pid} /T /F`, + `powershell -WindowStyle Hidden -NoProfile -Command "Stop-Process -Id ${pid} -Force -ErrorAction SilentlyContinue"`, { timeout: 5000, windowsHide: true }, () => { } ); diff --git a/scripts/installer.nsh b/scripts/installer.nsh index 39a7faaa2..261fd5032 100644 --- a/scripts/installer.nsh +++ b/scripts/installer.nsh @@ -3,6 +3,73 @@ ; Install: enables long paths, adds resources\cli to user PATH for openclaw CLI. ; Uninstall: removes the PATH entry and optionally deletes user data. +!macro customCheckAppRunning + ${GetProcessInfo} 0 $pid $1 $2 $3 $4 + ${if} $3 != "${APP_EXECUTABLE_FILENAME}" + ${if} ${isUpdated} + # allow app to exit without explicit kill + Sleep 300 + ${endIf} + + # Instead of launching cmd.exe /c tasklist, use the nsProcess plugin directly for all environments. + # This prevents the brief black cmd window from flashing. + ${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0 + + ${if} $R0 == 0 + ${if} ${isUpdated} + # allow app to exit without explicit kill + Sleep 1000 + Goto doStopProcess + ${endIf} + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "$(appRunning)" /SD IDOK IDOK doStopProcess + Quit + + doStopProcess: + DetailPrint `Closing running "${PRODUCT_NAME}"...` + + # Silently kill the process using nsProcess instead of taskkill / cmd.exe + ${nsProcess::KillProcess} "${APP_EXECUTABLE_FILENAME}" $R0 + + # to ensure that files are not "in-use" + Sleep 300 + + # Retry counter + StrCpy $R1 0 + + loop: + IntOp $R1 $R1 + 1 + + ${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0 + ${if} $R0 == 0 + # wait to give a chance to exit gracefully + Sleep 1000 + ${nsProcess::KillProcess} "${APP_EXECUTABLE_FILENAME}" $R0 + + ${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0 + ${If} $R0 == 0 + DetailPrint `Waiting for "${PRODUCT_NAME}" to close.` + Sleep 2000 + ${else} + Goto not_running + ${endIf} + ${else} + Goto not_running + ${endIf} + + # App likely running with elevated permissions. + # Ask user to close it manually + ${if} $R1 > 1 + MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeClosed)" /SD IDCANCEL IDRETRY loop + Quit + ${else} + Goto loop + ${endIf} + not_running: + nsProcess::Unload + ${endIf} + ${endIf} +!macroend + !macro customInstall ; Enable Windows long path support (Windows 10 1607+ / Windows 11). ; pnpm virtual store paths can exceed the default MAX_PATH limit of 260 chars.