I’m trying to get a Progress Bar to update within a loop but it’s not displaying it at all, even when it’s completed. I’m really stuck, can anyone help?
The code for the click event is:
$SyncHash.home_submit_Button.Add_Click({ if ($SyncHash.msgPath) { $SyncHash.threatLevel = 0 if ($SyncHash.msg) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($SyncHash.msg) } Get-Processing Process-Email Process-Results Start-Sleep -Milliseconds 50 #delay progress Get-Results $SyncHash.displayThreatLevel = 0 Start-Sleep -Milliseconds 50 $ScriptBlock = { Param($SyncHash) for($i = 0; $i -lt $SyncHash.threatLevel; $i++){Set-ProgressBar -Value $i} } try { Start-Job -ScriptBlock $ScriptBlock } catch [Exception] { Write-Debug $_ } } else { [System.Windows.MessageBox]::Show("Please specify the location of the email (Step 2).") } })
The ‘Set-ProgressBar’ function used above is:
function Set-ProgressBar { param( [string]$Value ) $SyncHash.Dispatcher.Invoke( [action]{$SyncHash.res_threatlevel_ProgressBar.Value = ("$($Value)`n")}, "Render" )
The full script (minus the XML):
#=========================================================================== # Main RunSpace #=========================================================================== $global:SyncHash = [hashtable]::Synchronized(@{}) $mainRunspace=[RunspaceFactory]::CreateRunspace() $mainRunspace.ApartmentState = "STA" $mainRunspace.ThreadOptions = "ReuseThread" $mainRunspace.Open() $mainRunspace.SessionStateProxy.SetVariable("SyncHash",$global:SyncHash) $psCmd = [PowerShell]::Create().AddScript({ Add-Type -AssemblyName presentationframework, presentationcore, windowsbase #=========================================================================== # Load XAML Objects In PowerShell #=========================================================================== function Get-XamlObject{ [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory = $true, ValuefromPipelineByPropertyName = $true, ValuefromPipeline = $true)] [Alias("FullName")] [System.String[]]$Path ) BEGIN { Set-StrictMode -Version Latest $wpfObjects = @{ } } #BEGIN PROCESS { try { foreach ($xamlFile in $Path) { #Change content of Xaml file to be a set of powershell GUI objects $inputXML = Get-Content -Path $xamlFile -ErrorAction Stop $inputXMLClean = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace 'x:Class=".*?"', '' -replace 'd:DesignHeight="\d*?"', '' -replace 'd:DesignWidth="\d*?"', '' [xml]$xaml = $inputXMLClean $reader = New-Object System.Xml.XmlNodeReader $xaml -ErrorAction Stop $tempform = [Windows.Markup.XamlReader]::Load($reader) #Grab named objects from tree and put in a flat structure $namedNodes = $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") $namedNodes | ForEach-Object { $SyncHash.Add($_.Name, $tempform.FindName($_.Name)) } #foreach-object } #foreach xamlpath } #try catch { throw $error[0] } #catch } #PROCESS END { Write-Output $SyncHash } #END } $path = '*removed*' Get-ChildItem -Path $path -Filter *.xaml -file | Where-Object { $_.Name -ne 'App.xaml' } | Get-XamlObject #================================================ #--------------------Load-GUI Cleanup------------ #================================================ Register-ObjectEvent -InputObject $mainRunspace ` -EventName 'AvailabilityChanged' ` -Action { if($Sender.RunspaceAvailability -eq "Available") { $Sender.Closeasync() $Sender.Dispose() } } #================================================ #--------------------Buttons--------------------- #================================================ #============================ # 'Home' Buttons #============================ $SyncHash.home_browse_Button.Add_Click({ $SyncHash.msgPath = Get-FilePath "C:\" $SyncHash.home_path_TextBox.IsEnabled = $true $SyncHash.home_path_TextBox.Text = $SyncHash.msgPath }) $SyncHash.home_clear_Button.Add_Click({ $SyncHash.home_path_TextBox.Text = "" }) $SyncHash.home_submit_Button.Add_Click({ function Update-Window { Param ( $Title, $Control, $Property, $Value, [switch]$AppendContent ) $SyncHash.$Control.Dispatcher.invoke([action]{ If ($PSBoundParameters['AppendContent']) { $SyncHash.$Control.AppendText($Value) } Else { $SyncHash.$Control.$Property = $Value } }, "Send") } if ($SyncHash.msgPath) { # [System.Windows.MessageBox]::Show($SyncHash.msgPath) $SyncHash.threatLevel = 0 # [System.Windows.MessageBox]::Show($SyncHash.threatLevel) if ($SyncHash.msg) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($SyncHash.msg) } Get-Processing # [System.Windows.MessageBox]::Show("processing?") Process-Email Process-Results # [System.Windows.MessageBox]::Show($SyncHash.threatLevel) Start-Sleep -Milliseconds 50 #delay progress Get-Results # [System.Windows.MessageBox]::Show("results?") $SyncHash.displayThreatLevel = 0 Start-Sleep -Milliseconds 50 #$SyncHash.res_threatlevel_ProgressBar.Dispatcher.InvokeAsync([action]{$SyncHash.res_threatlevel_ProgressBar.Value = $SyncHash.displayThreatLevel}) $ScriptBlock = { Param($SyncHash) #[System.Threading.Monitor]::Enter($SyncHash.SyncRoot) for($i = 0; $i -lt $SyncHash.threatLevel; $i++){Set-ProgressBar -Value $i} #[System.Threading.Monitor]::Exit($SyncHash.SyncRoot) } #Update-Window -Control res_threatLevel_ProgressBar -Property Value -Value $displayThreatLevel #$SyncHash.res_threatlevel_ProgressBar.Dispatcher.InvokeAsync([action]{$SyncHash.res_threatlevel_ProgressBar.Refresh()}) try { Start-Job -ScriptBlock $ScriptBlock } catch [Exception] { Write-Debug $_ } } else { [System.Windows.MessageBox]::Show("Please specify the location of the email (Step 2).") } # [System.Windows.MessageBox]::Show("end of submit") }) function Set-ProgressBar { param( [string]$Value ) $SyncHash.Dispatcher.Invoke( [action]{$SyncHash.res_threatlevel_ProgressBar.Value = ("$($Value)`n")}, "Render" ) } #=========================================================================== # Functions (Display) #=========================================================================== function Get-Home { $SyncHash.msgPath = "" $SyncHash.home_path_TextBox.Text = "e.g. C:\folder\example.msg" $SyncHash.home_path_TextBox.IsEnabled = $false $SyncHash.home_Grid.Visibility = 'Visible' $SyncHash.processing_Grid.Visibility = 'Collapsed' $SyncHash.results_Grid.Visibility = 'Collapsed' } #Show the processing window function Get-Processing { $SyncHash.home_Grid.Visibility = 'Collapsed' $SyncHash.processing_Grid.Visibility = 'Visible' $SyncHash.results_Grid.Visibility = 'Collapsed' } #Show the results window function Get-Results { $SyncHash.home_Grid.Visibility = 'Collapsed' $SyncHash.processing_Grid.Visibility = 'Collapsed' $SyncHash.results_Grid.Visibility = 'Visible' } function Update-ProgressBar { # [System.Windows.MessageBox]::Show("update progress bar") $SyncHash.res_threatlevel_ProgressBar.Dispatcher.InvokeAsync([action]{$SyncHash.res_threatlevel_ProgressBar.Value = $SyncHash.displayThreatLevel}) } #============================ # Functions (Processing) #============================ function Process-Email { $SyncHash.outlook = New-Object -comobject outlook.application $SyncHash.msg = $SyncHash.outlook.Session.OpenSharedItem($SyncHash.msgPath) $SyncHash.msg | Select body | ft -AutoSize $SyncHash.msgBody = $SyncHash.msg | Select-Object -ExpandProperty Body $SyncHash.msgSubject = $SyncHash.msg | Select-Object -ExpandProperty Subject $SyncHash.msgSenderName = $SyncHash.msg | Select-Object -ExpandProperty SenderName $SyncHash.msgSenderEmailAddress = $SyncHash.msg | Select-Object -ExpandProperty SenderEmailAddress $SyncHash.msgSubjectCheck = [regex]::Matches($SyncHash.msgSubject, "spam") | foreach {$_.Success} $SyncHash.msgSenderDomain = [regex]::Matches($SyncHash.msgSenderEmailAddress, "@[a-z0-9]*\.") | foreach {$_.Value} $SyncHash.msgSenderDomain = $SyncHash.msgSenderDomain -replace ".{1}$" $SyncHash.msgSenderDomain = $SyncHash.msgSenderDomain.Substring(1) $SyncHash.msgSenderCheck = [regex]::Matches($SyncHash.msgSenderName, "@[$_.msgSenderDomain]23") | foreach {$_.Success} if ($SyncHash.msgSubjectCheck -eq "Success") { $SyncHash.threatLevel += 20 } if ($SyncHash.msgSenderCheck -ne "Success") { $SyncHash.threatLevel += 30 } # [System.Windows.MessageBox]::Show("process email done") } function Process-Results { if ($SyncHash.threatLevel -ge 50) { $SyncHash.res_result_TextBlock.Text = "Malicious Email" } else { $SyncHash.res_result_TextBlock.Text = "Not detected as malicious" } $SyncHash.res_threatlevel_ProgressBar.Value = "0" # [System.Windows.MessageBox]::Show("process results done") } #================================================ #--------------------Events---------------------- #================================================ function Get-FilePath($initialDirectory){ $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.initialDirectory = $initialDirectory $OpenFileDialog.filter = "Outlook Item (*.msg)| *.msg" $OpenFileDialog.ShowDialog() | Out-Null $OpenFileDialog.filename } function Display-ThreatLevel { $SyncHash.res_threatlevel_ProgressBar.Dispatcher.Invoke([action]{$SyncHash.res_threatlevel_ProgressBar.Value = $SyncHash.displayThreatLevel}) $SyncHash.res_threatlevel_ProgressBar.Dispatcher.Invoke([action]{$SyncHash.res_threatlevel_ProgressBar.Refresh()}) [System.Windows.MessageBox]::Show($SyncHash.displayThreatLevel) } #=========================================================================== # Potentional functions for updating items #=========================================================================== function Update-Window { Param ( $Title, $Control, $Property, $Value, [switch]$AppendContent ) $SyncHash.$Control.Dispatcher.invokeasync([action]{ If ($PSBoundParameters['AppendContent']) { $SyncHash.$Control.AppendText($Value) } Else { $SyncHash.$Control.$Property = $Value } }, "Normal") } Function Update-Window_Old { Param ( $Control, $Property, $Value, [switch]$AppendContent ) # This is kind of a hack, there may be a better way to do this If ($Property -eq "Close") { $SyncHash.Window.Dispatcher.invoke([action]{$SyncHash.Window.Close()},"Normal") Return } # This updates the control based on the parameters passed to the function $SyncHash.$Control.Dispatcher.Invoke([action]{ # This bit is only really meaningful for the TextBox control, which might be useful for logging progress steps If ($PSBoundParameters['AppendContent']) { $SyncHash.$Control.AppendText($Value) } Else { $SyncHash.$Control.$Property = $Value } }, "Send") } #================================================ #---------------------Load-GUI------------------- #================================================ $SyncHash.Email_Check.ShowDialog() | Out-Null $SyncHash.Error = $Error }) $psCmd.Runspace = $mainRunspace $data = $psCmd.BeginInvoke()