fix(desktop): focus the update progress window, then hand focus to the relaunched Desktop
Two focus polish items from the first fully-working hand-off run (ryanc, 2026-08-09): 1. The progress window came up backgrounded: the script is spawned via `cmd start /min`, and Form.Show() + TopMost keeps it above other windows without ACTIVATING it. Claim activation explicitly (Form.Activate + SetForegroundWindow) right after Show. 2. The relaunched Desktop came up behind whatever the user had focused: a WMI-spawned process starts unfocused and cannot take foreground by itself. Since the hand-off owns foreground while its progress window is up, delegate it: AllowSetForegroundWindow(new pid), poll up to 20s for Electron's MainWindowHandle, then ShowWindow(SW_RESTORE) + SetForegroundWindow. Best-effort at every step -- a focus failure never affects the update result. Sequence on success: progress window foreground during the update -> window closes -> freshly relaunched Hermes.exe takes foreground. Verified live on the incident machine: Add-Type shim compiles under PS 5.1; WMI spawn + AllowSetForegroundWindow + MainWindowHandle poll + ShowWindow all execute against a real spawned window. (In the bg test shell SetForegroundWindow returns False by OS design -- only the current foreground owner may delegate; the real flow's TopMost progress window IS that owner.) PS parse clean, check-windows-footguns clean.
This commit is contained in:
parent
357b97eda6
commit
952f44f841
|
|
@ -50,6 +50,19 @@ param(
|
|||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
# Foreground helpers: the script is spawned via `cmd start /min`, so its
|
||||
# WinForms window comes up backgrounded unless we explicitly claim focus --
|
||||
# and after the update we must hand focus TO the relaunched Desktop (a
|
||||
# WMI-spawned process starts unfocused). AllowSetForegroundWindow lets us
|
||||
# pass our foreground right on to the new Hermes.exe pid.
|
||||
try {
|
||||
Add-Type -Namespace HermesHandoff -Name Win32 -MemberDefinition @'
|
||||
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(System.IntPtr hWnd);
|
||||
[DllImport("user32.dll")] public static extern bool AllowSetForegroundWindow(int dwProcessId);
|
||||
[DllImport("user32.dll")] public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow);
|
||||
'@ -ErrorAction Stop
|
||||
$script:Win32 = $true
|
||||
} catch { $script:Win32 = $false }
|
||||
# Render UTF-8 glyphs (checkmarks, arrows) correctly in our own console echo
|
||||
# too; the legacy conhost default OEM codepage shows them as mojibake.
|
||||
try {
|
||||
|
|
@ -106,6 +119,13 @@ function Show-ProgressWindow {
|
|||
$form.Controls.Add($bar)
|
||||
$form.Controls.Add($label)
|
||||
$form.Show()
|
||||
# `cmd start /min` spawned us backgrounded; TopMost keeps the window
|
||||
# above others but does not take activation. Claim it explicitly so
|
||||
# the progress window is what the user sees during the update.
|
||||
try {
|
||||
$form.Activate()
|
||||
if ($script:Win32) { [HermesHandoff.Win32]::SetForegroundWindow($form.Handle) | Out-Null }
|
||||
} catch {}
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
$script:Ui = [pscustomobject]@{ Form = $form; Box = $box }
|
||||
} catch {
|
||||
|
|
@ -172,6 +192,34 @@ function Start-DesktopRelaunch {
|
|||
if ($r -and $r.ReturnValue -eq 0) {
|
||||
Write-HandoffLog "desktop relaunched detached (pid $($r.ProcessId))"
|
||||
$spawned = $true
|
||||
# Hand our foreground rights to the new Desktop and focus its
|
||||
# main window once it exists. A WMI-spawned process starts
|
||||
# unfocused, and Windows only lets the CURRENT foreground
|
||||
# owner (us, while the progress window is up / just closed)
|
||||
# delegate that right. Poll briefly for the window: Electron
|
||||
# takes a couple seconds to create it.
|
||||
try {
|
||||
if ($script:Win32) {
|
||||
[HermesHandoff.Win32]::AllowSetForegroundWindow([int]$r.ProcessId) | Out-Null
|
||||
$deadline = (Get-Date).AddSeconds(20)
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
$hwnd = [System.IntPtr]::Zero
|
||||
try {
|
||||
$p = Get-Process -Id $r.ProcessId -ErrorAction Stop
|
||||
$hwnd = $p.MainWindowHandle
|
||||
} catch { break } # process died; nothing to focus
|
||||
if ($hwnd -ne [System.IntPtr]::Zero) {
|
||||
[HermesHandoff.Win32]::ShowWindow($hwnd, 9) | Out-Null # SW_RESTORE
|
||||
[HermesHandoff.Win32]::SetForegroundWindow($hwnd) | Out-Null
|
||||
Write-HandoffLog "focused relaunched desktop window"
|
||||
break
|
||||
}
|
||||
Start-Sleep -Milliseconds 400
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-HandoffLog "WARNING: could not focus relaunched desktop: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-HandoffLog "WARNING: WMI relaunch returned $($r.ReturnValue); falling back"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue