Running multithread ps script from task scheduler

Welcome Forums General PowerShell Q&A Running multithread ps script from task scheduler

This topic contains 4 replies, has 2 voices, and was last updated by

 
Participant
8 months ago.

  • Author
    Posts
  • #96180

    Participant
    Points: 13
    Rank: Member

    I have the below script that I need to run from a task schedule

    in powershell if I type c:\form\multiscreen.ps1 it runs fine

    In the cmd prompt if I type

    C:\Users\sbrookma>powershell.exe -executionpolicy bypass -file c:\form\multiscreen.ps1

    nothing happens

    Is this because it is opening up in a different thread ?

    
    $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
    This is a TextBlock control This is a TextBlock control
    with multiple lines of text. This is a TextBlock control
    3rd Line
    4th Line
    This is a TextBlock control This is a TextBlock control
    with multiple lines of text. This is a TextBlock control
    3rd Line
    4th Line
    This is a TextBlock control
    with multiple lines of text.
    3rd Line
    4th Line
    This is a TextBlock control This is a TextBlock control
    with multiple lines of text. This is a TextBlock control
    3rd Line
    4th Line
    This is a TextBlock control This is a TextBlock control
    with multiple lines of text. This is a TextBlock control
    3rd Line
    4th Line"                   
    
    Update-Display -control TextDisplay -Property Text -Value $DisplayText
    
    #region screenblocker
        Add-Type -AssemblyName System.Windows.Forms
        $screens = [System.Windows.Forms.Screen]::Allscreens | Where-Object Primary -eq $false
        $synchash.screens = New-Object System.Collections.Generic.List[System.Object]
        foreach($screen in $screens){ 
            $synchash.screens.Add($screen.devicename.replace('\','').replace('.','')[-1])
        }
        foreach ($screen in $screens){
            $synchash."$($screen.devicename.replace('\','').replace('.',''))" = $screen
            $newRunspace =[runspacefactory]::CreateRunspace()
            $newRunspace.ApartmentState = "STA"
            $newRunspace.ThreadOptions = "ReuseThread"
            $newRunspace.Open()
            $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
            $PowerShell = [PowerShell]::Create().AddScript({
                function LoadXaml ($filename){
                    $XamlLoader=(New-Object System.Xml.XmlDocument)
                    $XamlLoader.Load($filename)
                    return $XamlLoader
                }
                $screenNum = $synchash.screens[0]
                $display = "Display" + $screenNum
                $synchash.screens.RemoveAt(0)
    
                
                $XamlMainWindow = LoadXaml("\blankScreen.xaml")
                $reader = (New-Object System.Xml.XmlNodeReader $XamlMainWindow)
                $syncHash."Window$screenNum" = [Windows.Markup.XamlReader]::Load($reader)
            
            
            
                [xml]$XAML = $XamlMainWindow
                $XamlMainWindow.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object{
                #Find all of the form types and add them as members to the synchash
                $syncHash.Add($("" + $_.Name + $screenNum),$syncHash."Window$screenNum".FindName($_.Name) )
                }
    
                $syncHash."Window$screenNum".Top = $synchash.$display.bounds.Top;
                $syncHash."Window$screenNum".Left = $synchash.$display.bounds.Left;
                $syncHash."Window$screenNum".Width = $synchash.$display.bounds.Width;
                $syncHash."Window$screenNum".Height = $synchash.$display.bounds.Height;
                $synchash."Window$screenNum".Show()
    
                $synchash."error$screenNum" = $error
            })
            $PowerShell.Runspace = $newRunspace
            [void]$Jobs.Add((
                [pscustomobject]@{
                    PowerShell = $PowerShell
                    Runspace = $PowerShell.BeginInvoke()
                }
            ))
        }
        #endregion screenblocker
    
    
        $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 = "c:\form\multiping.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()
    
    
  • #96192

    Participant
    Points: 2
    Rank: Member

    Here's the code I use to get my scheduled task going. I've never tried running it from cmd to see what would happen, but I do know this works from task scheduler.

    $taskSettings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
            $taskTrigger = New-ScheduledTaskTrigger -AtLogOn
            $taskPrincipal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Users"
            $TaskArgs = @'
            -ExecutionPolicy Bypass -WindowStyle Hidden -command "& 'C:\Staging\CC2_User_Configs.ps1'"
    '@
            $taskAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $TaskArgs
            $task = New-ScheduledTask -Action $taskAction -Principal $taskPrincipal -Settings $taskSettings -Trigger $taskTrigger -Description Test
            Register-ScheduledTask -InputObject $task -TaskName CC2_User_Configs
    
  • #96218

    Participant
    Points: 13
    Rank: Member

    Hi Will, Thanks for that

    Using your code created the task but for some reason the arguments did not populate in the action tab so I added them manually. Unfortunately all that happened was that Powershell briefly opened then closed and did not run the script. But if I take the arguments out and login powershell opens and if I run the script from there it works.

  • #96227

    Participant
    Points: 2
    Rank: Member

    I had to add this to the end of my script. It may not be the only way to accomplish this, but what is happening is your main runspace for the script is finished, so it closes, even though your secondary runspaces have not finished.

    $psCmd.Runspace = $newRunspace
    $data = $psCmd.BeginInvoke()
    $loop = $true
    Start-Sleep 20
    while($loop)
    {
        start-sleep 2
        if($syncHash.Window.isVisible -eq $false)
        {
            $loop=$false
        }
    }
    
  • #96357

    Participant
    Points: 13
    Rank: Member

    Thanks Will that worked a treat 🙂

The topic ‘Running multithread ps script from task scheduler’ is closed to new replies.