# ToastHelper.ps1 — Windows toast notification helper (WinRT, no external modules) # Requires $InternalRoot to be defined in the calling script's scope before dot-sourcing. function Show-ToastNotification { <# .SYNOPSIS Shows a Windows toast notification with optional action buttons. .PARAMETER Buttons Array of hashtables with keys: Label — button text Action — URI to invoke (http://, https://, ms-settings:, etc.) Use empty string or 'dismiss' for a dismiss button. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Title, [Parameter(Mandatory)] [string]$Body, [object[]]$Buttons = @() ) try { # Load WinRT types. # Note: [TypeName, Assembly, ContentType=WindowsRuntime] is valid PowerShell 5.1+ syntax # for WinRT interop. Static parsers may flag it as an error — this is a known false positive. [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null # Use PowerShell's own AUMID — registered in Start Menu, ensures toast delivery $AppId = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe' # Build actions XML $actionsXml = '' if ($Buttons.Count -gt 0) { $actionsXml = '' foreach ($btn in $Buttons) { $label = [System.Security.SecurityElement]::Escape($btn.Label) $isDismiss = ([string]$btn.Action -eq '' -or [string]$btn.Action -eq 'dismiss') if ($isDismiss) { $actionsXml += "" } else { $action = [System.Security.SecurityElement]::Escape($btn.Action) $activationType = 'protocol' # covers http://, https://, ms-settings:, etc. $actionsXml += "" } } $actionsXml += '' } $escapedTitle = [System.Security.SecurityElement]::Escape($Title) $escapedBody = [System.Security.SecurityElement]::Escape($Body) $toastXml = @" $escapedTitle $escapedBody $actionsXml "@ $xml = New-Object Windows.Data.Xml.Dom.XmlDocument $xml.LoadXml($toastXml) $toast = New-Object Windows.UI.Notifications.ToastNotification $xml [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($AppId).Show($toast) Write-Log -Level Info -Message "Toast shown: '$Title'" -Feature 'ToastHelper' } catch { Write-Log -Level Error -Message "Failed to show toast '$Title': $_" -Feature 'ToastHelper' } }