# 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'
}
}