diff --git a/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 b/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 index 428eff6..04ea5ae 100644 --- a/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 +++ b/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 @@ -416,6 +416,33 @@ foreach ($app in $config.Applications) { continue } + # --- InUseCheck (partial Stage 2b): ForceClose / CloseAndReopen --- + # Before install, if the entry declares processes that would block the + # install, close them. Stage 2a supports ForceClose: polite WM_CLOSE + # with a timeout then hard Kill. "CloseAndReopen" is currently treated + # the same (Stage 2b will add the user-session relaunch trick). No + # reopen happens today; operator relaunches the app or the enforcer + # leaves it for Windows Explorer / Start-Menu shortcut behavior. + if ($app.InUseCheck -and $app.InUseCheck.Behavior -in @('ForceClose','CloseAndReopen')) { + foreach ($p in ($app.InUseCheck.Processes | Where-Object { $_ })) { + $procs = Get-Process -Name $p.Name -ErrorAction SilentlyContinue + foreach ($proc in $procs) { + $timeout = if ($p.GracefulCloseTimeoutSec) { [int]$p.GracefulCloseTimeoutSec } else { 10 } + try { + Write-InstallLog " InUseCheck: $($p.Name) (PID $($proc.Id)) asked to close (timeout ${timeout}s)" + $null = $proc.CloseMainWindow() + if (-not $proc.WaitForExit([int]($timeout * 1000))) { + Write-InstallLog " InUseCheck: $($p.Name) did not exit gracefully - killing" 'WARN' + $proc.Kill() + $proc.WaitForExit(5000) | Out-Null + } + } catch { + Write-InstallLog " InUseCheck: close/kill of $($p.Name) threw: $_" 'WARN' + } + } + } + } + $result = Invoke-InstallerAction -App $app $rc = $result.ExitCode