Private/Notification/Invoke-AsCurrentUser.ps1

function Invoke-AsCurrentUser {
    <#
    .SYNOPSIS
        Runs a script block as the currently logged-in user
    .DESCRIPTION
        Enables running UI code from SYSTEM context by executing in user session.
        Uses scheduled task technique for reliability.
    .PARAMETER ScriptBlock
        The script block to execute
    .PARAMETER Arguments
        Arguments to pass to the script block
    .PARAMETER Wait
        Wait for execution to complete
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ScriptBlock]$ScriptBlock,
        
        [Parameter()]
        [object[]]$Arguments,
        
        [Parameter()]
        [switch]$Wait
    )
    
    try {
        # Check if running as SYSTEM
        $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
        $isSystem = $currentUser.User.Value -eq 'S-1-5-18'
        
        if (-not $isSystem) {
            # Not SYSTEM - just run directly
            if ($Arguments) {
                return & $ScriptBlock @Arguments
            }
            else {
                return & $ScriptBlock
            }
        }
        
        # Running as SYSTEM - need to run in user context
        Write-PatchLog "Running script as current user from SYSTEM context" -Type Info
        
        # Get the currently logged-in user
        $loggedOnUser = Get-LoggedOnUser
        if (-not $loggedOnUser) {
            Write-PatchLog "No user logged in - cannot run in user context" -Type Warning
            return $null
        }
        
        # Create temp script file
        $tempScript = Join-Path $env:TEMP "PsPatchMyPC_UserScript_$(Get-Random).ps1"
        $ScriptBlock.ToString() | Out-File -FilePath $tempScript -Encoding UTF8 -Force
        
        try {
            # Create scheduled task to run as user
            $taskName = "PsPatchMyPC_UserTask_$(Get-Random)"
            $action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
                -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$tempScript`""
            
            $principal = New-ScheduledTaskPrincipal -UserId $loggedOnUser -LogonType Interactive -RunLevel Limited
            
            $task = Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Force
            
            # Start the task
            Start-ScheduledTask -TaskName $taskName
            
            if ($Wait) {
                # Wait for task to complete
                $timeout = 300  # 5 minutes
                $elapsed = 0
                do {
                    Start-Sleep -Seconds 1
                    $elapsed++
                    $taskInfo = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
                } while ($taskInfo.State -eq 'Running' -and $elapsed -lt $timeout)
            }
            
            # Clean up task
            Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
        }
        finally {
            # Clean up temp script
            Remove-Item -Path $tempScript -Force -ErrorAction SilentlyContinue
        }
    }
    catch {
        Write-PatchLog "Failed to run as current user: $_" -Type Error
        return $null
    }
}

function Get-LoggedOnUser {
    <#
    .SYNOPSIS
        Gets the currently logged-on interactive user
    #>

    [CmdletBinding()]
    param()
    
    try {
        # Get explorer.exe owner (reliable method to find logged-in user)
        $explorer = Get-WmiObject -Class Win32_Process -Filter "Name='explorer.exe'" -ErrorAction SilentlyContinue |
            Select-Object -First 1
        
        if ($explorer) {
            $owner = $explorer.GetOwner()
            if ($owner.Domain -and $owner.User) {
                return "$($owner.Domain)\$($owner.User)"
            }
            return $owner.User
        }
        
        # Fallback: query user session
        $sessions = query user 2>$null
        if ($sessions) {
            $activeSession = $sessions | Where-Object { $_ -match 'Active' } | Select-Object -First 1
            if ($activeSession -match '^\s*(\S+)') {
                return $Matches[1]
            }
        }
        
        return $null
    }
    catch {
        Write-Verbose "Failed to get logged on user: $_"
        return $null
    }
}

function Test-UserSessionActive {
    <#
    .SYNOPSIS
        Tests if there's an active user session
    #>

    [CmdletBinding()]
    param()
    
    $user = Get-LoggedOnUser
    return ($null -ne $user)
}