mirror of
https://github.com/hansjone/netx.git
synced 2026-10-14 23:40:54 +08:00
Windows PowerShell 5.1 Start-Process -Wait (and `& exe` with captured output) waits for every descendant / until the stdout pipe closes. The postgres and NetX processes a start leaves running made runtime_ctl.ps1 never return: tray stuck on "Starting", Start Menu "Start NetX" never opened the UI. Start-NetxPowerShell -Wait, the UAC relaunch and the Setup wait in check_update now wait for the process itself (WaitForExit). Service mode in runtime_ctl: start via sc.exe without blocking on SERVICE_RUNNING; stop writes the supervisor stop flag, polls up to 120s, then kills the WinSW tree as a last resort. Verified in an offline Windows Sandbox (no VC++ redist, no Python). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
3.1 KiB
PowerShell
80 lines
3.1 KiB
PowerShell
param(
|
|
[ValidateSet("start", "stop", "restart")]
|
|
[string]$Action = "start",
|
|
[string]$ProgramRoot = "",
|
|
[string]$DataRoot = ""
|
|
)
|
|
|
|
# Single start/stop/restart entry for tray + shortcuts. Routes through the
|
|
# Windows service / SYSTEM task when one is installed, else the plain scripts.
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
. "$PSScriptRoot\_common.ps1"
|
|
Assert-NetxAdminOrRelaunch -ScriptPath $PSCommandPath -BoundParameters $PSBoundParameters -WindowStyle Hidden
|
|
|
|
$prog = Get-NetxProgramRoot -Override $ProgramRoot
|
|
$data = Get-NetxDataRoot -ProgramRoot $prog -Override $DataRoot
|
|
|
|
function Invoke-NetxChildScript([string]$Name, [string[]]$Extra = @()) {
|
|
# Never read the child's stdout: postgres / NetX processes it starts inherit the
|
|
# pipe, so `& powershell.exe` with captured output blocks until they exit (forever).
|
|
# Start-Process -Wait is no good either (it waits for the whole process tree).
|
|
$p = Start-NetxPowerShell -File (Join-Path $PSScriptRoot $Name) `
|
|
-Arguments (@("-ProgramRoot", $prog, "-DataRoot", $data) + $Extra) `
|
|
-WorkingDirectory $prog -WindowStyle Hidden -PassThru
|
|
$null = $p.Handle # keep a handle so ExitCode is available after exit
|
|
$p.WaitForExit()
|
|
return $p.ExitCode
|
|
}
|
|
|
|
function Stop-NetxService {
|
|
# Stop-Service waits forever on a service that will not stop; poll with a deadline
|
|
# and kill the WinSW process tree as a last resort.
|
|
$svc = Get-Service -Name "NetX" -ErrorAction SilentlyContinue
|
|
if (-not $svc -or $svc.Status -eq "Stopped") { return }
|
|
# Ask service_run.ps1 to exit ourselves rather than relying only on WinSW's stop hook.
|
|
Write-NetxServiceStopFlag -DataRoot $data
|
|
& sc.exe stop NetX | Out-Null
|
|
$deadline = (Get-Date).AddSeconds(120)
|
|
while ((Get-Date) -lt $deadline) {
|
|
Start-Sleep -Seconds 1
|
|
$svc.Refresh()
|
|
if ($svc.Status -eq "Stopped") { return }
|
|
}
|
|
$wmi = Get-CimInstance Win32_Service -Filter "Name='NetX'" -ErrorAction SilentlyContinue
|
|
if ($wmi -and $wmi.ProcessId) { & taskkill.exe /PID $wmi.ProcessId /T /F | Out-Null }
|
|
$null = Invoke-NetxChildScript "stop_netx_app.ps1"
|
|
}
|
|
|
|
function Start-NetxService {
|
|
# Don't block on SERVICE_RUNNING: callers wait for /health instead.
|
|
$svc = Get-Service -Name "NetX" -ErrorAction SilentlyContinue
|
|
if ($svc -and $svc.Status -eq "Stopped") { & sc.exe start NetX | Out-Null }
|
|
}
|
|
|
|
$runner = Get-NetxManagedRunner
|
|
switch ($runner) {
|
|
"service" {
|
|
if ($Action -ne "start") { Stop-NetxService }
|
|
if ($Action -ne "stop") { Start-NetxService }
|
|
}
|
|
"task" {
|
|
if ($Action -ne "start") {
|
|
Stop-ScheduledTask -TaskName "NetXService" -TaskPath "\NetX\"
|
|
$null = Invoke-NetxChildScript "stop_netx_app.ps1"
|
|
}
|
|
if ($Action -ne "stop") {
|
|
Start-ScheduledTask -TaskName "NetXService" -TaskPath "\NetX\"
|
|
}
|
|
}
|
|
default {
|
|
if ($Action -ne "start") {
|
|
$null = Invoke-NetxChildScript "stop_netx_app.ps1"
|
|
}
|
|
if ($Action -ne "stop") {
|
|
$code = Invoke-NetxChildScript "start_netx_app.ps1" @("-SkipBrowser")
|
|
if ($code) { exit $code }
|
|
}
|
|
}
|
|
}
|
|
exit 0
|