Multi monitor script

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

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.

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