functions/Trace.ps1
Function Trace-Message { [cmdletbinding(DefaultParameterSetName = "message")] [alias("trace")] [OutputType("none")] Param( [Parameter(HelpMessage = "Specify a title for the trace window.", ParameterSetName = "init")] [ValidateNotNullOrEmpty()] [string]$Title = "Trace Messages", [Parameter(HelpMessage = "Specify a background color for the trace window", ParameterSetName = "init")] [ValidateNotNullOrEmpty()] [string]$BackgroundColor = "#FFFFF8DC", [Parameter(HelpMessage = "Specify the width of the trace window.", ParameterSetName = "init")] [ValidateNotNullOrEmpty()] [int32]$Width = 800, [Parameter(HelpMessage = "Specify the width of the trace window.", ParameterSetName = "init")] [ValidateNotNullOrEmpty()] [int32]$Height = 500, [Parameter(Position = 0, Mandatory,HelpMessage = "Specify a message to write to the trace window.", ValueFromPipeline, ParameterSetName = "message")] [string]$Message ) Begin { Write-Verbose "Starting $($MyInvocation.MyCommand)" Function _SetTraceMessage { [cmdletbinding()] Param( [Parameter(Mandatory, ValueFromPipeline)] [string]$Message ) Begin { $hash = $global:traceSynchHash } Process { Write-Verbose "Adding message $message" $content = "$((Get-Date).TimeOfDay) - $message" $hash.TextBox.Dispatcher.Invoke([action] { $hash.TextBox.AppendText("$Content`n") }, "Normal") } End {} } } #begin Process { #must be running Windows if ($IsLinux -OR $IsMacOS) { Write-Warning "This command only works on Windows platforms." #bail out return } Write-Verbose "Using parameter set $($PSCmdlet.ParameterSetName)" if ($global:TraceEnabled) { if ($PSCmdlet.ParameterSetName -eq 'init') { Write-Verbose "Defining synchronized hashtable `$global:traceSynchHash" $global:traceSynchHash = [hashtable]::Synchronized(@{Date=(Get-Date)}) Write-Verbose "Initializing a new runspace" $newRunspace = [RunspaceFactory]::CreateRunspace() $newRunspace.ApartmentState = "STA" $newRunspace.ThreadOptions = "ReuseThread" Write-Verbose "Open the new runspace" $newRunspace.Open() Write-Verbose "Setting synchronized hashtable variable" $newRunspace.SessionStateProxy.SetVariable("traceSynchHash", $global:traceSynchHash) $newRunspace.SessionStateProxy.GetVariable("traceSynchHash") | Out-String | Write-Verbose $formParams = @{ Title = $title BackgroundColor = $BackgroundColor Width = $width Height = $Height } Write-Verbose "Creating runspace script" $psCmd = [PowerShell]::Create().AddScript( { Param([string]$Title, [object]$BackgroundColor, [int]$width, [int]$height) Add-Type -AssemblyName PresentationFramework -ErrorAction stop Add-Type -AssemblyName PresentationCore -ErrorAction stop Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop $form = New-Object System.Windows.Window $form.Title = $Title $form.Height = $height $form.Width = $width $form.ResizeMode = "noResize" $form.Background = $BackgroundColor $Form.Left = 100 $form.Top = 100 #$form.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen $grid = New-Object System.Windows.Controls.Grid $text = New-Object System.Windows.Controls.TextbBox $text.text = "" #$text.Text = "Starting...`n" $text.Padding = 5 $text.Width = $form.width - 25 $text.Height = $form.Height - 100 $text.Margin = "5,5,5,5" $text.TextWrapping = [System.Windows.TextWrapping]::Wrap $text.VerticalAlignment = "top" $text.IsEnabled = $True $text.AcceptsReturn = $True $text.VerticalScrollBarVisibility = "Auto" $text.FontFamily = "Consolas" $text.HorizontalAlignment = "stretch" $text.VerticalAlignment = "top" $grid.AddChild($text) $Quit = New-Object System.Windows.Controls.Button $quit.Content = "_Quit" $quit.Width = 75 $quit.Height = 25 $quit.Margin = "10,10,0,10" $quit.HorizontalAlignment = "Left" $quit.VerticalAlignment = "bottom" $quit.Add_click( { $form.close() $traceSynchHash.clear() Remove-Variable -name traceSynchHash -Scope global }) $grid.AddChild($quit) $Save = New-Object System.Windows.Controls.Button $save.Content = "_Save" $save.Width = 75 $save.Height = 25 $save.Margin = "10,10,10,10" $save.HorizontalAlignment = "right" $save.VerticalAlignment = "bottom" $save.Add_click( { $SaveDialog = New-Object System.Windows.Forms.SaveFileDialog $SaveDialog.InitialDirectory = "$ENV:Temp" $SaveDialog.Filter = "txt Files (*.txt)|*.txt |All files (*.*)|*.*" [void]$SaveDialog.ShowDialog() if ($SaveDialog.Filename) { $text.text | Out-File -FilePath $SaveDialog.filename [System.Windows.Forms.MessageBox]::Show("Trace log exported to $($SaveDialog.Filename)", "Trace Export | Trace Message") } }) $grid.AddChild($Save) $form.AddChild($grid) #add keys to the synchronized hashtable $traceSynchHash.TextBox = $text $traceSynchHash.Form = $Form #show the form [void]$traceSynchHash.Form.ShowDialog() $traceSynchHash.Error = $Error }) #add parameters [void]$psCmd.AddParameters($formParams) $psCmd.Runspace = $newRunspace Write-Verbose "Invoking runspace command" $handle = $psCmd.BeginInvoke() Write-Verbose "Using this global synchronized hashtable" $global:traceSynchHash | Out-String | Write-Verbose #wait for hashtable to initialize with runspace values $i=0 do { $i++ start-sleep -Seconds 1 #uncomment for troubleshooting #Write-host $i -ForegroundColor yellow -NoNewline } Until ($global:traceSynchHash.TextBox) Write-Verbose "Creating a runspace cleanup job" [void](New-RunspaceCleanupJob -Handle $handle -powerShell $pscmd -sleep 30 -PassThru) Write-Verbose "Getting trace metadata" $elevated = ([Security.Principal.WindowsPrincipal]([security.principal.windowsidentity]::Getcurrent())).IsInRole([system.security.principal.securityidentifier]"S-1-5-32-544") $os = Get-CimInstance -ClassName Win32_OperatingSystem -Property Caption, Version, OSArchitecture $init = @( "*************************************************", "User : $($env:USERDOMAIN)\$($env:USERNAME)", "Elevated : $Elevated", "Computer : $env:Computername", "PSVersion : $($PSVersionTable.PSVersion)", "OS : $($os.caption)", "Version : $($os.version)", "Architecture : $($os.OSArchitecture)", "*************************************************" ) Write-Verbose "Init" $init | Write-Verbose Write-Verbose "Setting trace metadata" $init | _SetTraceMessage } #if init else { Write-Verbose "Setting message $message" _SetTraceMessage -Message $Message } } else { Write-Verbose "TraceEnabled is set to False" } } #process End { Write-Verbose "Ending $($MyInvocation.MyCommand)" } #end } |