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()