diff --git a/apps/desktop/electron/wsl-path-bridge.ts b/apps/desktop/electron/wsl-path-bridge.ts index 74b991643c..feff32a2f7 100644 --- a/apps/desktop/electron/wsl-path-bridge.ts +++ b/apps/desktop/electron/wsl-path-bridge.ts @@ -86,6 +86,11 @@ export function resolveDefaultWslDistro(): string { const out = execFileSync('wsl.exe', ['-l', '-q'], { encoding: 'utf8', env: { ...process.env, WSL_UTF8: '1' }, + // On WSL-less machines wsl.exe prints "The Windows Subsystem for Linux + // is not installed..." to stderr; stderr is inherited by default, so + // that banner leaks into whatever console the app is attached to + // (visible e.g. during the update hand-off). Discard it. (#80184) + stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000, windowsHide: true }) diff --git a/scripts/desktop-update/windows.ps1 b/scripts/desktop-update/windows.ps1 index f0bfd52839..5787519495 100644 --- a/scripts/desktop-update/windows.ps1 +++ b/scripts/desktop-update/windows.ps1 @@ -571,6 +571,59 @@ function Start-DesktopRelaunch { } catch { Write-HandoffLog "WARNING: WMI relaunch failed: $($_.Exception.Message); falling back" } + if (-not $spawned) { + # Middle rung: explorer.exe-mediated launch. On some machines + # Win32_Process.Create fails outright (observed ReturnValue 8, + # "unknown failure"), and the tethered fallback below re-attaches the + # Desktop to this console — its stdout then floods the console and the + # window can't close while the app lives. Explorer re-parents the + # target exactly like a normal shell launch, giving the same + # no-console detachment WMI would have. Explorer returns no pid, so + # verify by watching for a fresh Hermes process. + try { + $exeName = [System.IO.Path]::GetFileNameWithoutExtension($RelaunchExe) + $before = @(Get-Process -Name $exeName -ErrorAction SilentlyContinue | ForEach-Object { $_.Id }) + Start-Process -FilePath 'explorer.exe' -ArgumentList ('"{0}"' -f $RelaunchExe) | Out-Null + $explorerDeadline = (Get-Date).AddSeconds(15) + while ((Get-Date) -lt $explorerDeadline) { + $fresh = @(Get-Process -Name $exeName -ErrorAction SilentlyContinue | Where-Object { $before -notcontains $_.Id }) + if ($fresh.Count -gt 0) { + Write-HandoffLog "desktop relaunched detached via explorer (pid $($fresh[0].Id))" + $spawned = $true + # Same foreground hand-off as the WMI rung: the new process + # starts unfocused and only the current foreground owner + # (us) can delegate that right. + try { + if ($script:Win32) { + [HermesHandoff.Win32]::AllowSetForegroundWindow([int]$fresh[0].Id) | Out-Null + $focusDeadline = (Get-Date).AddSeconds(20) + while ((Get-Date) -lt $focusDeadline) { + $hwnd = [System.IntPtr]::Zero + try { $hwnd = (Get-Process -Id $fresh[0].Id -ErrorAction Stop).MainWindowHandle } catch { break } + 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)" + } + break + } + Start-Sleep -Milliseconds 400 + if ($script:Ui) { [System.Windows.Forms.Application]::DoEvents() } + } + if (-not $spawned) { + Write-HandoffLog "WARNING: explorer relaunch did not produce a $exeName process; falling back" + } + } catch { + Write-HandoffLog "WARNING: explorer relaunch failed: $($_.Exception.Message); falling back" + } + } if (-not $spawned) { try { # Fallback keeps the old behavior (console tie-in and all) --