Author Posts

March 14, 2018 at 2:58 pm

Hi, I have copied / pasted / fudged together the script below and now need some advice on the next bit. Basically the script will be launched by a scheduled task created by SCCM to install a firmware upgrade of the users machine. When they click install it calls another ps script that actually does the upgrade. The issue I have is that we have a lot of users with multiple screens and their main screen will not always be the top most left in a pod or their left screen in on the desk. I have thought of generating a blank form that is a mile high by a mile wide to cover all of their screens but it will miss the top left or left screen if this is not the main screen. Also I dont know where to insert the blank form generation in the code below as it is my first play with XAML forms. Any help would be great
please note I have taken out the below to make it easier to debug 🙂
WindowState="Maximized"
WindowStyle="None"

$Global:syncHash = [hashtable]::Synchronized(@{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)

# Load WPF assembly if necessary
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')

$psCmd = [PowerShell]::Create().AddScript({
[xml]$xaml = @"

"@

$reader=(New-Object System.Xml.XmlNodeReader $xaml)

$syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )

[xml]$XAML = $xaml
$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
#Find all of the form types and add them as members to the synchash
$syncHash.Add($_.Name,$syncHash.Window.FindName($_.Name) )

}

$Script:JobCleanup = [hashtable]::Synchronized(@{})
$Script:Jobs = [system.collections.arraylist]::Synchronized((New-Object System.Collections.ArrayList))

#region Background runspace to clean up jobs
$jobCleanup.Flag = $True
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("jobCleanup",$jobCleanup)
$newRunspace.SessionStateProxy.SetVariable("jobs",$jobs)
$jobCleanup.PowerShell = [PowerShell]::Create().AddScript({
#Routine to handle completed runspaces
Do {
Foreach($runspace in $jobs) {
If ($runspace.Runspace.isCompleted) {
[void]$runspace.powershell.EndInvoke($runspace.Runspace)
$runspace.powershell.dispose()
$runspace.Runspace = $null
$runspace.powershell = $null
}
}
#Clean out unused runspace jobs
$temphash = $jobs.clone()
$temphash | Where {
$_.runspace -eq $Null
} | ForEach {
$jobs.remove($_)
}
Start-Sleep -Seconds 1
} while ($jobCleanup.Flag)
})
$jobCleanup.PowerShell.Runspace = $newRunspace
$jobCleanup.Thread = $jobCleanup.PowerShell.BeginInvoke()
#endregion Background runspace to clean up jobs
Function Update-Display {
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
}
}, "Normal")
}

$DisplayText = "

This is a TextBlock control
with multiple lines of text.
3rd Line
4th Line"

Update-Display -control TextDisplay -Property Text -Value $DisplayText

$syncHash.Install.Add_Click({

$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("SyncHash",$SyncHash)
$PowerShell = [PowerShell]::Create().AddScript({

Function Update-Window {
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
}
}, "Normal")
}

start-sleep -Milliseconds 850

$scripttorun = "some powershell script.ps1"

update-window -Control Progress -Property Value -Value 25

start-sleep -Milliseconds 850
update-window -Control Progress -Property Value -Value 50
Invoke-Expression $scripttorun

start-sleep -Milliseconds 500
update-window -Control Progress -Property Value -Value 75

start-sleep -Milliseconds 200
update-window -Control Progress -Property Value -Value 100
})
$PowerShell.Runspace = $newRunspace
[void]$Jobs.Add((
[pscustomobject]@{
PowerShell = $PowerShell
Runspace = $PowerShell.BeginInvoke()
}
))
})

#region Window Close
$syncHash.Window.Add_Closed({
Write-Verbose 'Halt runspace cleanup job processing'
$jobCleanup.Flag = $False

#Stop all runspaces
$jobCleanup.PowerShell.Dispose()
})
#endregion Window Close

$syncHash.Window.ShowDialog() | Out-Null
$syncHash.Error = $Error
})

$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()

March 14, 2018 at 3:38 pm

I responded to your other post. Looks like it is in the review queue for the moderator, but you should be able to see it soon.

March 14, 2018 at 4:07 pm

Just popped it in. Spam queue is acting weird lately.