Files
genshin-assistant/scripts/kill-stale-instances.ps1
T
2026-07-07 22:02:24 +02:00

90 lines
3.7 KiB
PowerShell

# Beendet alle laufenden Prozesse dieses Projekts (Electron, Node/Vite, und
# einen verwaisten lokalen Input-Helper-Kindprozess), bevor eine neue Instanz
# startet.
#
# Warum das noetig ist: Ein normales Schliessen der App (X-Button) raeumt
# sauber auf (main.ts: app.on("will-quit") beendet den gespawnten
# input-helper.ps1-Kindprozess). Wird die App aber gewaltsam beendet - z.B.
# ueber Task-Manager "Task beenden" oder ein Skript, das Stop-Process -Force
# auf den Electron-Prozess anwendet - greift dieser Cleanup-Handler NICHT,
# und der Kindprozess (PowerShell mit dem Input-Helper) bleibt als Waise
# aktiv, obwohl das Hauptfenster laengst weg ist. Dieses Skript faengt genau
# das ab: es wird vor JEDEM Start (npm run dev und npm run dev:admin, ueber
# den "predev"-Hook) ausgefuehrt und raeumt vorherige Instanzen kompromisslos
# weg, egal wie sie beendet wurden.
$ErrorActionPreference = "Stop"
$project = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path
$electronPath = Join-Path $project "node_modules\electron\dist\electron.exe"
$devControlPort = 17317
$killed = 0
$candidatePids = @{}
try {
$health = Invoke-RestMethod -Method Get -Uri "http://127.0.0.1:$devControlPort/health" -TimeoutSec 2 -ErrorAction Stop
if ($health.appBuild -and $health.appBuild.cwd -eq $project) {
Write-Host "Dev-Control-Port $devControlPort gehoert zu diesem Projekt (PID $($health.appBuild.pid), Signatur $($health.appBuild.signature)). Versuche Self-Shutdown..."
try {
Invoke-RestMethod -Method Get -Uri "http://127.0.0.1:$devControlPort/dev/shutdown?reason=restart" -TimeoutSec 2 -ErrorAction Stop | Out-Null
Start-Sleep -Milliseconds 900
} catch {
Write-Host "Self-Shutdown nicht verfuegbar oder fehlgeschlagen: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
} catch {
# No dev-control server or an older/stuck process; continue with process scan.
}
Get-CimInstance Win32_Process |
Where-Object {
($_.Name -eq "electron.exe" -and $_.ExecutablePath -eq $electronPath) -or
($_.Name -eq "node.exe" -and $_.CommandLine -like "*$project*") -or
($_.Name -eq "powershell.exe" -and $_.CommandLine -like "*input-helper.ps1*")
} |
ForEach-Object {
$candidatePids[[int]$_.ProcessId] = $_.Name
}
try {
Get-NetTCPConnection -LocalPort $devControlPort -State Listen -ErrorAction Stop |
ForEach-Object {
if ($_.OwningProcess -gt 0) {
$owner = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
$candidatePids[[int]$_.OwningProcess] = if ($owner) { "$($owner.ProcessName) port $devControlPort" } else { "port $devControlPort owner" }
}
}
} catch {
# Get-NetTCPConnection can be unavailable on some machines; process matching
# above still handles the normal non-elevated path.
}
$failed = 0
foreach ($entry in $candidatePids.GetEnumerator()) {
Write-Host "Beende alte Instanz: $($entry.Value) (PID $($entry.Key))"
try {
Stop-Process -Id $entry.Key -Force -ErrorAction Stop
$killed++
} catch {
Write-Host "WARNUNG: Konnte PID $($entry.Key) nicht beenden: $($_.Exception.Message)" -ForegroundColor Yellow
$failed++
}
}
if ($killed -gt 0) {
# Windows braucht einen Moment, um Ports/Handles wirklich freizugeben.
Start-Sleep -Milliseconds 500
Write-Host "$killed alte Prozess(e) beendet."
}
if ($failed -gt 0) {
Write-Host "$failed alte Prozess(e) konnten nicht beendet werden. Wenn das ein Admin-Prozess ist, starte npm run dev:admin und bestaetige UAC oder schliesse die alte App manuell." -ForegroundColor Yellow
throw "$failed alte Prozess(e) konnten nicht beendet werden."
}
if ($killed -eq 0 -and $failed -eq 0) {
Write-Host "Keine alten Instanzen gefunden."
} else {
Start-Sleep -Milliseconds 300
}