Install-FromManifest: InUseCheck ForceClose / CloseAndReopen
Adds partial Stage 2b support for InUseCheck entries in manifests. When an entry declares InUseCheck.Behavior = ForceClose or CloseAndReopen and the listed processes are running at install time, the lib now: 1. Calls CloseMainWindow() on each matching Process handle (polite WM_CLOSE). 2. Waits GracefulCloseTimeoutSec (default 10) for exit. 3. Hard-kills the process if it did not exit gracefully. 4. Proceeds with the install. "CloseAndReopen" is currently treated the same as ForceClose - no reopen happens today. Stage 2b will add the user-session scheduled-task trick to relaunch the closed app in the logged-in user's session. In practice for the 24/7 ShopFloor persistent-user pattern the operator relaunches the app manually (or the app is registered as Startup and reopens on the next reboot), which is acceptable. Concrete impact: the eDNC entry in standard-machine/manifest.json lists InUseCheck.Processes = DncMain + NTLARS with Behavior=CloseAndReopen. On a retrofit or upgrade cycle that finds eDNC 6.4.3 needs to go to 6.4.5, the lib now force-closes DncMain and NTLARS before msiexec rather than risking Restart Manager silently scheduling a pending-file-replace that does not actually upgrade until the next reboot (which on a 24/7 PC might be never). Verified in the Win11 analyzer VM against manifests declaring InUseCheck on eDNC - logs show "InUseCheck: DncMain (PID ...) asked to close" followed by either graceful exit or the force-kill path, then install proceeds without 3010. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user