private/Controls/Invoke-OnUIThread.ps1
|
function Invoke-OnUIThread { <# .SYNOPSIS Marshals code execution to the UI dispatcher thread. Used to safely update WPF controls from background threads #> [CmdletBinding()] param( [Parameter(Mandatory)] [scriptblock]$ScriptBlock, [switch]$Async ) $session = Get-UiSession $dispatcher = $null # Try session window first, then fall back to Application.Current if ($session -and $session.Window) { $dispatcher = $session.Window.Dispatcher } if ($null -eq $dispatcher) { $dispatcher = [System.Windows.Application]::Current.Dispatcher } if ($null -eq $dispatcher) { return & $ScriptBlock } if ($dispatcher.CheckAccess()) { return & $ScriptBlock } if ($Async) { [void]$dispatcher.BeginInvoke([Action]$ScriptBlock, $null) return } # BeginInvoke + Wait avoids the deadlock that a direct Invoke causes here $operation = $dispatcher.BeginInvoke([Func[object]]$ScriptBlock, $null) try { $operation.Wait() } catch [System.AggregateException] { # OperationCanceledException = dispatcher isn't running (tests, no UI window) $inner = $_.Exception.InnerException if ($inner -is [System.OperationCanceledException]) { return & $ScriptBlock } if ($inner) { throw $inner } throw } # Wait() may not surface every failure mode, so inspect status as well switch ($operation.Status) { 'Completed' { return $operation.Result } 'Faulted' { $ex = $operation.Exception if ($ex.InnerException) { $ex = $ex.InnerException } throw $ex } 'Aborted' { return & $ScriptBlock } default { return $null } } } |