Private/Set-WindowVisible.ps1
|
# SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (C) 2026 Tyler Vigario <# Show or hide a window, and CONFIRM IT BY OBSERVATION. ShowWindow's return value cannot be used for this, and neither can GetLastError. Measured on the reference host 2026-07-29, both directions returned false: unelevated -> elevated window : returned false, error 5, window did NOT move elevated -> normal window : returned false, error 1461, window DID move The return value is documented as the window's PREVIOUS visibility, not success, so it is false for every attach -- because every attach starts from hidden. The only trustworthy signal is whether IsWindowVisible actually changed. Everything about a hidden session is invisible by construction, so reporting "attached" without checking is exactly how a no-op gets mistaken for success. Returns $true on confirmed change, $false otherwise. Diagnosis of a failure is the caller's to report, because only the caller knows whether it is mid-pipeline. #> function Set-WindowVisible { # No ShouldProcess here. The gate belongs to the public command, which calls # ShouldProcess after resolving and before reaching this -- a second gate would # either be dead code or prompt twice for one action. This function is private and # unreachable from outside the module, so there is no caller it could protect. [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory)][IntPtr]$Handle, [Parameter(Mandatory)][bool]$Show ) $before = Test-WindowVisible -Handle $Handle [Greenroom.Win1]::ShowWindow($Handle, $(if ($Show) { $script:SW_RESTORE } else { $script:SW_HIDE })) | Out-Null if ($Show) { [Greenroom.Win1]::SetForegroundWindow($Handle) | Out-Null } # The window manager is asynchronous; IsWindowVisible immediately after the call # can still report the old state. Start-Sleep -Milliseconds 250 $after = Test-WindowVisible -Handle $Handle Write-Verbose "window $Handle visible before=$before after=$after (wanted $Show)" return ($after -eq $Show) } <# Why a window operation most likely failed, as a sentence. Split out so Show- and Hide- report identically without duplicating the reasoning, and so the guess about UIPI is made in one place. #> function Get-WindowFailureReason { param([Parameter(Mandatory)][string]$Name) $msg = "the window for '$Name' did not change state. The session is unaffected; only the window operation failed." if (-not (Test-SelfElevated)) { $msg += " Most likely cause: the window belongs to a higher-integrity process and UIPI refused the call, which it does silently. Re-run from an elevated shell." } return $msg } |