GUI-Runspaces-PoshRSJob module

This topic contains 8 replies, has 5 voices, and was last updated by  Linus 4 weeks, 1 day ago.

  • Author
    Posts
  • #82318

    Linus
    Participant

    Hello all,

    A bit of introduction

    I just started messing around with the world of powershell and unfortunately i am in deep water and would like your help guidance.
    The problem is that i do not have skills in programming so i can only go with the trial and error method to learn for now.
    The "masterplan" 🙂 is to provide a GUI to users to do multiple automated actions(e.g check free hard disk space,kill processes etc) in a pool of servers (100 and rising up fast). I have figured out how to do some basic stuff in a sequential way but because of the number of servers will rise up i have to implement a way of multitasking..

    Problems experiencing using GUI

    GUI freezes when a long running operation takes place and becomes unresponsive(imagine a long operation for each server)

    Below i will present a very simple form utilizing powershell jobs in order to achieve multitasking(baby steps) that updates a progressbar.
    I have used powershell studio and export in a ps1 format in order for you to test.
    This is working OK 🙂 !! (I used the built-in functions in powershell so no big deal).
    I could press both buttons almost at the same time with no GUI freezing and checking with process explorer that two powershell.exe sub-processes are created..

    
    #----------------------------------------------
    # Generated Form Function
    #----------------------------------------------
    function Call-powershell-job-tracker_psf {
    
    	#----------------------------------------------
    	#region Import the Assemblies
    	#----------------------------------------------
    	[void][reflection.assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
    	[void][reflection.assembly]::Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
    	[void][reflection.assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
    	#endregion Import Assemblies
    
    	#----------------------------------------------
    	#region Define SAPIEN Types
    	#----------------------------------------------
    	try{
    		$local:type = [ProgressBarOverlay]
    	}
    	catch
    	{
    		Add-Type -ReferencedAssemblies ('System.Windows.Forms', 'System.Drawing') -TypeDefinition  @" 
    		using System;
    		using System.Windows.Forms;
    		using System.Drawing;
            namespace SAPIENTypes
            {
    		    public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
    	        {
    	            protected override void WndProc(ref Message m)
    	            { 
    	                base.WndProc(ref m);
    	                if (m.Msg == 0x000F)// WM_PAINT
    	                {
    	                    if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                            {
                                using (Graphics g = this.CreateGraphics())
                                {
                                    using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                    {
                                        stringFormat.Alignment = StringAlignment.Center;
                                        stringFormat.LineAlignment = StringAlignment.Center;
                                        if (!string.IsNullOrEmpty(this.Text))
                                            g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                        else
                                        {
                                            int percent = (int)(((double)Value / (double)Maximum) * 100);
                                            g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                        }
                                    }
                                }
                            }
    	                }
    	            }
                  
                    public string TextOverlay
                    {
                        get
                        {
                            return base.Text;
                        }
                        set
                        {
                            base.Text = value;
                            Invalidate();
                        }
                    }
    	        }
            }
    "@ | Out-Null
    	}
    	#endregion Define SAPIEN Types
    
    	#----------------------------------------------
    	#region Generated Form Objects
    	#----------------------------------------------
    	[System.Windows.Forms.Application]::EnableVisualStyles()
    	$form1 = New-Object 'System.Windows.Forms.Form'
    	$progressbaroverlay2 = New-Object 'SAPIENTypes.ProgressBarOverlay'
    	$buttonStartJob2 = New-Object 'System.Windows.Forms.Button'
    	$progressbaroverlay1 = New-Object 'SAPIENTypes.ProgressBarOverlay'
    	$buttonStartJob = New-Object 'System.Windows.Forms.Button'
    	$buttonOK = New-Object 'System.Windows.Forms.Button'
    	$imagelistButtonBusyAnimation = New-Object 'System.Windows.Forms.ImageList'
    	$timerJobTracker = New-Object 'System.Windows.Forms.Timer'
    	$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
    	#endregion Generated Form Objects
    	
    	
    	
    	#----------------------------------------------
    	# User Generated Script
    	#----------------------------------------------
    	
    	$form1_Load={
    		#TODO: Initialize Form Controls here
    		
    	}
    	
    	$buttonStartJob_Click={
    		
    		$buttonStartJob.Enabled = $false	
    		
    		#Create a New Job using the Job Tracker
    		Add-JobTracker -Name 'JobName' `
    		-JobScript {
    	   		#--------------------------------------------------
    			#TODO: Set a script block
    			#Important: Do not access form controls from this script block.
    	    
    			Param($Argument1)#Pass any arguments using the ArgumentList parameter
    			
    			for ($i = 0; $i -lt 10; $i++)
    			{
    				Start-Sleep -Milliseconds 1000
    				#Output Progress
    				$i + 1
    			} 
    			
    			#--------------------------------------------------
    		}`
    		-CompletedScript {
    			Param($Job)
    			#$results = Receive-Job -Job $Job 
    			#Enable the Button
    			$buttonStartJob.ImageIndex = -1
    			$buttonStartJob.Enabled = $true
    		}`
    		-UpdateScript {
    			Param($Job)
    			#$results = Receive-Job -Job $Job -Keep
    			$results = Receive-Job -Job $Job | Select-Object -Last 1
    			
    			if ($results -is [int])
    			{
    							
    				$progressbaroverlay1.Maximum = 10 - 1
    				$progressbaroverlay1.Value = $results
    			}
    			#Animate the Button
    			if($null -ne $buttonStartJob.ImageList)
    			{
    				if($buttonStartJob.ImageIndex -lt $buttonStartJob.ImageList.Images.Count - 1)
    				{
    					$buttonStartJob.ImageIndex += 1
    				}
    				else
    				{
    					$buttonStartJob.ImageIndex = 0		
    				}
    			}
    		}`
    		-ArgumentList $null
    	}
    	
    	$jobTracker_FormClosed=[System.Windows.Forms.FormClosedEventHandler]{
    	#Event Argument: $_ = [System.Windows.Forms.FormClosedEventArgs]
    		#Stop any pending jobs
    		Stop-JobTracker
    	}
    	
    	$timerJobTracker_Tick={
    		Update-JobTracker
    	}
    	
    	#region Job Tracker
    	$JobTrackerList = New-Object System.Collections.ArrayList
    	function Add-JobTracker
    	{
    		
    		
    		Param(
    		[ValidateNotNull()]
    		[Parameter(Mandatory=$true)]
    		[string]$Name, 
    		[ValidateNotNull()]
    		[Parameter(Mandatory=$true)]
    		[ScriptBlock]$JobScript,
    		$ArgumentList = $null,
    		[ScriptBlock]$CompletedScript,
    		[ScriptBlock]$UpdateScript)
    		
    		#Start the Job
    		$job = Start-Job -Name $Name -ScriptBlock $JobScript -ArgumentList $ArgumentList
    		
    		if($null -ne $job)
    		{
    			#Create a Custom Object to keep track of the Job & Script Blocks
    			$members = @{	'Job' = $Job;
    							'CompleteScript' = $CompletedScript;
    							'UpdateScript' = $UpdateScript}
    			
    			$psObject = New-Object System.Management.Automation.PSObject -Property $members
    			
    			[void]$JobTrackerList.Add($psObject)	
    			
    			#Start the Timer
    			if(-not $timerJobTracker.Enabled)
    			{
    				$timerJobTracker.Start()
    			}
    		}
    		elseif($null -ne $CompletedScript)
    		{
    			#Failed
    			Invoke-Command -ScriptBlock $CompletedScript -ArgumentList $null
    		}
    	
    	}
    	
    	function Update-JobTracker
    	{
    		
    		
    		#Poll the jobs for status updates
    		$timerJobTracker.Stop() #Freeze the Timer
    		
    		for($index = 0; $index -lt $JobTrackerList.Count; $index++)
    		{
    			$psObject = $JobTrackerList[$index]
    			
    			if($null -ne $psObject) 
    			{
    				if($null -ne $psObject.Job)
    				{
    					if ($psObject.Job.State -eq 'Blocked')
    	                {
    	                    #Try to unblock the job
    	                    Receive-Job $psObject.Job | Out-Null
    	                }
    	                elseif($psObject.Job.State -ne 'Running')
    					{				
    						#Call the Complete Script Block
    						if($null -ne $psObject.CompleteScript)
    						{
    							#$results = Receive-Job -Job $psObject.Job
    							Invoke-Command -ScriptBlock $psObject.CompleteScript -ArgumentList $psObject.Job
    						}
    						
    						$JobTrackerList.RemoveAt($index)
    						Remove-Job -Job $psObject.Job
    						$index-- #Step back so we don't skip a job
    					}
    					elseif($null -ne $psObject.UpdateScript)
    					{
    						#Call the Update Script Block
    						Invoke-Command -ScriptBlock $psObject.UpdateScript -ArgumentList $psObject.Job
    					}
    				}
    			}
    			else
    			{
    				$JobTrackerList.RemoveAt($index)
    				$index-- #Step back so we don't skip a job
    			}
    		}
    		
    		if($JobTrackerList.Count -gt 0)
    		{
    			$timerJobTracker.Start()#Resume the timer	
    		}	
    	}
    	
    	function Stop-JobTracker
    	{
    		
    		#Stop the timer
    		$timerJobTracker.Stop()
    		
    		#Remove all the jobs
    		while($JobTrackerList.Count -gt 0)
    		{
    			$job = $JobTrackerList[0].Job
    			$JobTrackerList.RemoveAt(0)
    			Stop-Job $job
    			Remove-Job $job
    		}
    	}
    	#endregion
    	
    	$buttonStartJob2_Click={
    		
    		$buttonStartJob2.Enabled = $false	
    		
    		#Create a New Job using the Job Tracker
    		Add-JobTracker -Name 'JobName' `
    		-JobScript {
    	   		#--------------------------------------------------
    			#TODO: Set a script block
    			#Important: Do not access form controls from this script block.
    	    
    			Param($Argument1)#Pass any arguments using the ArgumentList parameter
    			
    			for ($i = 0; $i -lt 10; $i++)
    			{
    				Start-Sleep -Milliseconds 1000
    				#Output Progress
    				$i + 1
    			}
    			
    			#--------------------------------------------------
    		}`
    		-CompletedScript {
    			Param($Job)
    			#$results = Receive-Job -Job $Job 
    			#Enable the Button
    			$buttonStartJob2.ImageIndex = -1
    			$buttonStartJob2.Enabled = $true
    		}`
    		-UpdateScript {
    			Param($Job)
    			#$results = Receive-Job -Job $Job -Keep
    			
    			$results2 = Receive-Job -Job $Job | Select-Object -Last 1
    			
    			if ($results2 -is [int])
    			{
    				
    				$progressbaroverlay2.Maximum = 10 - 1
    				$progressbaroverlay2.Value = $results2
    			}
    			
    			#Animate the Button
    			if($null -ne $buttonStartJob2.ImageList)
    			{
    				if($buttonStartJob2.ImageIndex -lt $buttonStartJob2.ImageList.Images.Count - 1)
    				{
    					$buttonStartJob2.ImageIndex += 1
    				}
    				else
    				{
    					$buttonStartJob2.ImageIndex = 0		
    				}
    			}
    		}`
    		-ArgumentList $null
    	}
    	
    	# --End User Generated Script--
    	#----------------------------------------------
    	#region Generated Events
    	#----------------------------------------------
    	
    	$Form_StateCorrection_Load=
    	{
    		#Correct the initial state of the form to prevent the .Net maximized form issue
    		$form1.WindowState = $InitialFormWindowState
    	}
    	
    	$Form_Cleanup_FormClosed=
    	{
    		#Remove all event handlers from the controls
    		try
    		{
    			$buttonStartJob2.remove_Click($buttonStartJob2_Click)
    			$buttonStartJob.remove_Click($buttonStartJob_Click)
    			$form1.remove_FormClosed($jobTracker_FormClosed)
    			$form1.remove_Load($form1_Load)
    			$timerJobTracker.remove_Tick($timerJobTracker_Tick)
    			$form1.remove_Load($Form_StateCorrection_Load)
    			$form1.remove_FormClosed($Form_Cleanup_FormClosed)
    		}
    		catch [Exception]
    		{ }
    	}
    	#endregion Generated Events
    
    	#----------------------------------------------
    	#region Generated Form Code
    	#----------------------------------------------
    	$form1.SuspendLayout()
    	#
    	# form1
    	#
    	$form1.Controls.Add($progressbaroverlay2)
    	$form1.Controls.Add($buttonStartJob2)
    	$form1.Controls.Add($progressbaroverlay1)
    	$form1.Controls.Add($buttonStartJob)
    	$form1.Controls.Add($buttonOK)
    	$form1.AcceptButton = $buttonOK
    	$form1.AutoScaleDimensions = '6, 13'
    	$form1.AutoScaleMode = 'Font'
    	$form1.ClientSize = '424, 333'
    	$form1.FormBorderStyle = 'FixedDialog'
    	$form1.MaximizeBox = $False
    	$form1.MinimizeBox = $False
    	$form1.Name = 'form1'
    	$form1.StartPosition = 'CenterScreen'
    	$form1.Text = 'Form'
    	$form1.add_FormClosed($jobTracker_FormClosed)
    	$form1.add_Load($form1_Load)
    	#
    	# progressbaroverlay2
    	#
    	$progressbaroverlay2.Location = '50, 193'
    	$progressbaroverlay2.Name = 'progressbaroverlay2'
    	$progressbaroverlay2.Size = '100, 23'
    	$progressbaroverlay2.TabIndex = 2
    	#
    	# buttonStartJob2
    	#
    	$buttonStartJob2.ImageList = $imagelistButtonBusyAnimation
    	$buttonStartJob2.Location = '50, 151'
    	$buttonStartJob2.Name = 'buttonStartJob2'
    	$buttonStartJob2.Size = '75, 23'
    	$buttonStartJob2.TabIndex = 0
    	$buttonStartJob2.Text = 'Start2'
    	$buttonStartJob2.TextImageRelation = 'ImageBeforeText'
    	$buttonStartJob2.UseVisualStyleBackColor = $True
    	$buttonStartJob2.add_Click($buttonStartJob2_Click)
    	#
    	# progressbaroverlay1
    	#
    	$progressbaroverlay1.Location = '283, 193'
    	$progressbaroverlay1.Name = 'progressbaroverlay1'
    	$progressbaroverlay1.Size = '100, 23'
    	$progressbaroverlay1.TabIndex = 1
    	#
    	# buttonStartJob
    	#
    	$buttonStartJob.ImageList = $imagelistButtonBusyAnimation
    	$buttonStartJob.Location = '283, 151'
    	$buttonStartJob.Name = 'buttonStartJob'
    	$buttonStartJob.Size = '75, 23'
    	$buttonStartJob.TabIndex = 0
    	$buttonStartJob.Text = 'Start'
    	$buttonStartJob.TextImageRelation = 'ImageBeforeText'
    	$buttonStartJob.UseVisualStyleBackColor = $True
    	$buttonStartJob.add_Click($buttonStartJob_Click)
    	#
    	# buttonOK
    	#
    	$buttonOK.Anchor = 'Bottom, Right'
    	$buttonOK.DialogResult = 'OK'
    	$buttonOK.Location = '337, 298'
    	$buttonOK.Name = 'buttonOK'
    	$buttonOK.Size = '75, 23'
    	$buttonOK.TabIndex = 0
    	$buttonOK.Text = '&OK'
    	$buttonOK.UseVisualStyleBackColor = $True
    	#
    	# imagelistButtonBusyAnimation
    	#
    	$Formatter_binaryFomatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    	#region Binary Data
    	$System_IO_MemoryStream = New-Object System.IO.MemoryStream (,[byte[]][System.Convert]::FromBase64String('
    AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAu
    MC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAA
    ACZTeXN0ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkD
    AAAADwMAAAB2CgAAAk1TRnQBSQFMAgEBCAEAATABAAEwAQABEAEAARABAAT/ASEBAAj/AUIBTQE2
    BwABNgMAASgDAAFAAwABMAMAAQEBAAEgBgABMP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/
    AP8AugADwgH/Az0B/wM9Af8DwgH/MAADwgH/A10B/wOCAf8DwgH/sAADPQH/AwAB/wMAAf8DPQH/
    MAADggH/Az0B/wM9Af8DXQH/gAADwgH/Az0B/wM9Af8DwgH/IAADPQH/AwAB/wMAAf8DPQH/A8IB
    /wNdAf8DggH/A8IB/xAAA8IB/wM9Af8DPQH/A8IB/wNdAf8DPQH/Az0B/wNdAf8EAAOSAf8DkgH/
    A8IB/3AAAz0B/wMAAf8DAAH/Az0B/yAAA8IB/wM9Af8DPQH/A8IB/wOCAf8DPQH/Az0B/wOCAf8Q
    AAM9Af8DAAH/AwAB/wM9Af8DwgH/A10B/wOCAf8DwgH/A5IB/wOCAf8DggH/A5IB/3AAAz0B/wMA
    Af8DAAH/Az0B/zAAA10B/wM9Af8DPQH/A10B/xAAAz0B/wMAAf8DAAH/Az0B/xAAA5IB/wOSAf8D
    kgH/A8IB/3AAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wNdAf8DggH/A8IB/xAAA8IB/wM9Af8DPQH/
    A8IB/xAAA8IB/wOSAf8DkgH/A8IB/zgAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wOCAf8DXQH/A8IB
    /zAAA8IB/wPCAf8DkgH/A8IB/zQAA8IB/wPCAf80AAM9Af8DAAH/AwAB/wM9Af8wAANdAf8DPQH/
    Az0B/wNdAf8wAAOSAf8DggH/A4IB/wOSAf8wAAPCAf8DwgH/A8IB/wPCAf8wAAM9Af8DAAH/AwAB
    /wM9Af8wAAOCAf8DPQH/Az0B/wOCAf8wAAPCAf8DggH/A5IB/wOSAf8wAAPCAf8DwgH/A8IB/wPC
    Af8wAAPCAf8DPQH/Az0B/wPCAf8wAAPCAf8DggH/A10B/wPCAf8wAAPCAf8DkgH/A5IB/wPCAf80
    AAPCAf8DwgH/EAADwgH/A8IB/xQAA8IB/wOCAf8DXQH/A8IB/zAAA8IB/wOSAf8DkgH/A8IB/zQA
    A8IB/wPCAf9UAAPCAf8DwgH/A8IB/wPCAf8QAANdAf8DPQH/Az0B/wNdAf8wAAOSAf8DggH/A5IB
    /wOSAf8wAAPCAf8DwgH/A8IB/wPCAf9QAAPCAf8DwgH/A8IB/wPCAf8DwgH/A8IB/wOSAf8DwgH/
    A4IB/wM9Af8DPQH/A4IB/yQAA8IB/wPCAf8EAAPCAf8DggH/A5IB/wOSAf8wAAPCAf8DwgH/A8IB
    /wPCAf9UAAPCAf8DwgH/BAADkgH/A4IB/wOCAf8DkgH/A8IB/wOCAf8DXQH/A8IB/yAAA8IB/wPC
    Af8DwgH/A8IB/wPCAf8DkgH/A5IB/wPCAf80AAPCAf8DwgH/ZAADkgH/A5IB/wOSAf8DkgH/MAAD
    wgH/A8IB/wPCAf8DwgH/sAADwgH/A5IB/wOSAf8DwgH/NAADwgH/A8IB/7QAA8IB/wPCAf8DkgH/
    A8IB/zQAA8IB/wPCAf+0AAOSAf8DggH/A4IB/wOSAf8wAAPCAf8DwgH/A8IB/wPCAf+gAAPCAf8D
    XQH/A4IB/wPCAf8DkgH/A5IB/wOSAf8DwgH/BAADwgH/A8IB/xQAA8IB/wPCAf8DkgH/A8IB/wPC
    Af8DwgH/A8IB/wPCAf8kAAPCAf8DwgH/dAADggH/Az0B/wM9Af8DggH/A8IB/wOSAf8DkgH/A8IB
    /wPCAf8DwgH/A8IB/wPCAf8QAAOSAf8DggH/A4IB/wOSAf8EAAPCAf8DwgH/JAADwgH/A8IB/wPC
    Af8DwgH/cAADXQH/Az0B/wM9Af8DggH/EAADwgH/A8IB/wPCAf8DwgH/EAADkgH/A5IB/wOSAf8D
    kgH/MAADwgH/A8IB/wPCAf8DwgH/cAADwgH/A10B/wNdAf8DwgH/FAADwgH/A8IB/xQAA8IB/wOS
    Af8DkgH/A8IB/zQAA8IB/wPCAf9sAAPCAf8DPQH/Az0B/wPCAf8wAAPCAf8DXQH/A4IB/wPCAf8w
    AAPCAf8DwgH/A5IB/wPCAf80AAPCAf8DwgH/NAADPQH/AwAB/wMAAf8DPQH/MAADggH/Az0B/wM9
    Af8DXQH/MAADkgH/A4IB/wOCAf8DkgH/MAADwgH/A8IB/wPCAf8DwgH/MAADPQH/AwAB/wMAAf8D
    PQH/MAADXQH/Az0B/wM9Af8DggH/MAADkgH/A5IB/wOSAf8DkgH/MAADwgH/A8IB/wPCAf8DwgH/
    MAADwgH/Az0B/wM9Af8DwgH/MAADwgH/A10B/wNdAf8DwgH/MAADwgH/A5IB/wOSAf8DwgH/NAAD
    wgH/A8IB/3wAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wNdAf8DggH/A8IB/zAAA8IB/wPCAf8DkgH/
    A8IB/xAAA8IB/wM9Af8DPQH/A8IB/1AAAz0B/wMAAf8DAAH/Az0B/zAAA4IB/wM9Af8DPQH/A10B
    /zAAA5IB/wOCAf8DggH/A5IB/xAAAz0B/wMAAf8DAAH/Az0B/1AAAz0B/wMAAf8DAAH/Az0B/zAA
    A10B/wM9Af8DPQH/A4IB/wOSAf8DPQH/Az0B/wPCAf8gAAOSAf8DkgH/A5IB/wOSAf8DwgH/A10B
    /wOCAf8DwgH/Az0B/wMAAf8DAAH/Az0B/1AAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wOCAf8DXQH/
    A8IB/wM9Af8DAAH/AwAB/wM9Af8gAAPCAf8DkgH/A5IB/wPCAf8DggH/Az0B/wM9Af8DXQH/A8IB
    /wM9Af8DPQH/A8IB/6AAAz0B/wMAAf8DAAH/Az0B/zAAA10B/wM9Af8DPQH/A4IB/7AAA8IB/wM9
    Af8DPQH/A8IB/zAAA8IB/wOCAf8DXQH/A8IB/xgAAUIBTQE+BwABPgMAASgDAAFAAwABMAMAAQEB
    AAEBBQABgAEBFgAD/4EABP8B/AE/AfwBPwT/AfwBPwH8AT8D/wHDAfwBAwHAASMD/wHDAfwBAwHA
    AQMD/wHDAf8DwwP/AcMB/wPDAf8B8AH/AfAB/wHwAf8B+QH/AfAB/wHwAf8B8AH/AfAB/wHwAf8B
    8AH/AfAB/wHwAf8B8AH/AfAB/wHwAf8B+QHnAcMB/wHDAf8B5wL/AsMB/wHDAf8BwwL/AcABAwH+
    AUMB/wHDAv8B5AEDAfwBAwH/AecC/wH8AT8B/AE/BP8B/AE/Af4BfwT/AfwBPwH+AX8E/wH8AT8B
    /AE/BP8BwAEnAcABPwHnA/8BwAEDAcIBfwHDA/8DwwH/AcMD/wHDAecBwwH/AecD/wEPAf8BDwH/
    AQ8B/wGfAf8BDwH/AQ8B/wEPAf8BDwH/AQ8B/wEPAf8BDwH/AQ8B/wEPAf8BDwH/AQ8B/wGfA/8B
    wwH/AcMB/wLDAv8BwwH/AcMB/wLDAv8BwwH/AcABPwHAAQMC/wHDAf8BwAE/AcABAwT/AfwBPwH8
    AT8E/wH8AT8B/AE/Cw=='))
    	#endregion
    	$imagelistButtonBusyAnimation.ImageStream = $Formatter_binaryFomatter.Deserialize($System_IO_MemoryStream)
    	$Formatter_binaryFomatter = $null
    	$System_IO_MemoryStream = $null
    	$imagelistButtonBusyAnimation.TransparentColor = 'Transparent'
    	#
    	# timerJobTracker
    	#
    	$timerJobTracker.add_Tick($timerJobTracker_Tick)
    	$form1.ResumeLayout()
    	#endregion Generated Form Code
    
    	#----------------------------------------------
    
    	#Save the initial state of the form
    	$InitialFormWindowState = $form1.WindowState
    	#Init the OnLoad event to correct the initial state of the form
    	$form1.add_Load($Form_StateCorrection_Load)
    	#Clean up the control events
    	$form1.add_FormClosed($Form_Cleanup_FormClosed)
    	#Show the Form
    	return $form1.ShowDialog()
    
    } #End Function
    
    #Call the form
    Call-powershell-job-tracker_psf | Out-Null
    

    PoshRSJob Implementation

    The problem with powershell jobs is that there is no throttling and i believe there is a BIG overhead creating a new process for each job.This led me to chaotic world of runspaces.I found this excellent module of PoshRSJob (https://github.com/proxb/PoshRSJob that uses runspaces so you can multithread easier in Powershell.

    I tried this very simplistic approach to import the PoshRSJob and replace the commands of powershell jobs with the ones of the module.(I told you i just started so cannot do much,be kind to me :)).
    The result is to have the commands running BUT two problems

    1.The progress bars are not updated
    2.When i close the form The GUI leaves a zombie process when compiled as exe using Powershell studio

    Here is the implementation of PoshRSJob insted of powershell jobs

     Import-Module -Name PoshRSJob -Force
    
    #----------------------------------------------
    # Generated Form Function
    #----------------------------------------------
    function Call-powershell-job-tracker-PoshRSJob_psf
    {
    	
    	#----------------------------------------------
    	#region Import the Assemblies
    	#----------------------------------------------
    	[void][reflection.assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
    	[void][reflection.assembly]::Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
    	[void][reflection.assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
    	#endregion Import Assemblies
    	
    	#----------------------------------------------
    	#region Define SAPIEN Types
    	#----------------------------------------------
    	try
    	{
    		$local:type = [ProgressBarOverlay]
    	}
    	catch
    	{
    		Add-Type -ReferencedAssemblies ('System.Windows.Forms', 'System.Drawing') -TypeDefinition  @" 
    		using System;
    		using System.Windows.Forms;
    		using System.Drawing;
            namespace SAPIENTypes
            {
    		    public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
    	        {
    	            protected override void WndProc(ref Message m)
    	            { 
    	                base.WndProc(ref m);
    	                if (m.Msg == 0x000F)// WM_PAINT
    	                {
    	                    if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                            {
                                using (Graphics g = this.CreateGraphics())
                                {
                                    using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                    {
                                        stringFormat.Alignment = StringAlignment.Center;
                                        stringFormat.LineAlignment = StringAlignment.Center;
                                        if (!string.IsNullOrEmpty(this.Text))
                                            g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                        else
                                        {
                                            int percent = (int)(((double)Value / (double)Maximum) * 100);
                                            g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                        }
                                    }
                                }
                            }
    	                }
    	            }
                  
                    public string TextOverlay
                    {
                        get
                        {
                            return base.Text;
                        }
                        set
                        {
                            base.Text = value;
                            Invalidate();
                        }
                    }
    	        }
            }
    "@ | Out-Null
    	}
    	#endregion Define SAPIEN Types
    	
    	#----------------------------------------------
    	#region Generated Form Objects
    	#----------------------------------------------
    	[System.Windows.Forms.Application]::EnableVisualStyles()
    	$form1 = New-Object 'System.Windows.Forms.Form'
    	$progressbaroverlay2 = New-Object 'SAPIENTypes.ProgressBarOverlay'
    	$buttonStartJob2 = New-Object 'System.Windows.Forms.Button'
    	$progressbaroverlay1 = New-Object 'SAPIENTypes.ProgressBarOverlay'
    	$buttonStartJob = New-Object 'System.Windows.Forms.Button'
    	$buttonOK = New-Object 'System.Windows.Forms.Button'
    	$imagelistButtonBusyAnimation = New-Object 'System.Windows.Forms.ImageList'
    	$timerJobTracker = New-Object 'System.Windows.Forms.Timer'
    	$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
    	#endregion Generated Form Objects
    	
    	
    	
    	#----------------------------------------------
    	# User Generated Script
    	#----------------------------------------------
    	
    	$form1_Load = {
    		#TODO: Initialize Form Controls here
    		
    	}
    	
    	$buttonStartJob_Click = {
    		
    		$buttonStartJob.Enabled = $false
    		
    		#Create a New Job using the Job Tracker
    		Add-JobTracker -Name 'JobName' `
    					   -JobScript {
    			#--------------------------------------------------
    			#TODO: Set a script block
    			#Important: Do not access form controls from this script block.
    			
    			Param ($Argument1) #Pass any arguments using the ArgumentList parameter
    			
    			for ($i = 0; $i -lt 10; $i++)
    			{
    				Start-Sleep -Milliseconds 1000
    				#Output Progress
    				$i + 1
    			}
    			
    			#--------------------------------------------------
    		}`
    					   -CompletedScript {
    			Param ($Job)
    			#$results = Receive-Job -Job $Job 
    			#Enable the Button
    			$buttonStartJob.ImageIndex = -1
    			$buttonStartJob.Enabled = $true
    		}`
    					   -UpdateScript {
    			Param ($Job)
    			#$results = Receive-Job -Job $Job -Keep
    			$results = Receive-RSJob -Job $Job | Select-Object -Last 1
    			
    			if ($results -is [int])
    			{
    				
    				$progressbaroverlay1.Maximum = 10 - 1
    				$progressbaroverlay1.Value = $results
    			}
    			#Animate the Button
    			if ($null -ne $buttonStartJob.ImageList)
    			{
    				if ($buttonStartJob.ImageIndex -lt $buttonStartJob.ImageList.Images.Count - 1)
    				{
    					$buttonStartJob.ImageIndex += 1
    				}
    				else
    				{
    					$buttonStartJob.ImageIndex = 0
    				}
    			}
    		}`
    					   -ArgumentList $null
    	}
    	
    	$jobTracker_FormClosed = [System.Windows.Forms.FormClosedEventHandler]{
    		#Event Argument: $_ = [System.Windows.Forms.FormClosedEventArgs]
    		#Stop any pending jobs
    		Stop-JobTracker
    	}
    	
    	$timerJobTracker_Tick = {
    		Update-JobTracker
    	}
    	
    	#region Job Tracker
    	$JobTrackerList = New-Object System.Collections.ArrayList
    	function Add-JobTracker
    	{
    		
    		
    		Param (
    			[ValidateNotNull()]
    			[Parameter(Mandatory = $true)]
    			[string]$Name,
    			[ValidateNotNull()]
    			[Parameter(Mandatory = $true)]
    			[ScriptBlock]$JobScript,
    			$ArgumentList = $null,
    			[ScriptBlock]$CompletedScript,
    			[ScriptBlock]$UpdateScript)
    		
    		#Start the Job
    		$job = Start-RSJob -Name $Name -ScriptBlock $JobScript -ArgumentList $ArgumentList
    		
    		if ($null -ne $job)
    		{
    			#Create a Custom Object to keep track of the Job & Script Blocks
    			$members = @{
    				'Job' = $Job;
    				'CompleteScript' = $CompletedScript;
    				'UpdateScript' = $UpdateScript
    			}
    			
    			$psObject = New-Object System.Management.Automation.PSObject -Property $members
    			
    			[void]$JobTrackerList.Add($psObject)
    			
    			#Start the Timer
    			if (-not $timerJobTracker.Enabled)
    			{
    				$timerJobTracker.Start()
    			}
    		}
    		elseif ($null -ne $CompletedScript)
    		{
    			#Failed
    			Invoke-Command -ScriptBlock $CompletedScript -ArgumentList $null
    		}
    		
    	}
    	
    	function Update-JobTracker
    	{
    		
    		
    		#Poll the jobs for status updates
    		$timerJobTracker.Stop() #Freeze the Timer
    		
    		for ($index = 0; $index -lt $JobTrackerList.Count; $index++)
    		{
    			$psObject = $JobTrackerList[$index]
    			
    			if ($null -ne $psObject)
    			{
    				if ($null -ne $psObject.Job)
    				{
    					if ($psObject.Job.State -eq 'Blocked')
    					{
    						#Try to unblock the job
    						Receive-RSJob $psObject.Job | Out-Null
    					}
    					elseif ($psObject.Job.State -ne 'Running')
    					{
    						#Call the Complete Script Block
    						if ($null -ne $psObject.CompleteScript)
    						{
    							#$results = Receive-Job -Job $psObject.Job
    							Invoke-Command -ScriptBlock $psObject.CompleteScript -ArgumentList $psObject.Job
    						}
    						
    						$JobTrackerList.RemoveAt($index)
    						Remove-RSJob -Job $psObject.Job
    						$index-- #Step back so we don't skip a job
    					}
    					elseif ($null -ne $psObject.UpdateScript)
    					{
    						#Call the Update Script Block
    						Invoke-Command -ScriptBlock $psObject.UpdateScript -ArgumentList $psObject.Job
    					}
    				}
    			}
    			else
    			{
    				$JobTrackerList.RemoveAt($index)
    				$index-- #Step back so we don't skip a job
    			}
    		}
    		
    		if ($JobTrackerList.Count -gt 0)
    		{
    			$timerJobTracker.Start() #Resume the timer	
    		}
    	}
    	
    	function Stop-JobTracker
    	{
    		
    		#Stop the timer
    		$timerJobTracker.Stop()
    		
    		#Remove all the jobs
    		while ($JobTrackerList.Count -gt 0)
    		{
    			$job = $JobTrackerList[0].Job
    			$JobTrackerList.RemoveAt(0)
    			Stop-RSJob $job
    			Remove-RSJob $job
    		}
    	}
    	#endregion
    	
    	$buttonStartJob2_Click = {
    		
    		$buttonStartJob2.Enabled = $false
    		
    		#Create a New Job using the Job Tracker
    		Add-JobTracker -Name 'JobName' `
    					   -JobScript {
    			#--------------------------------------------------
    			#TODO: Set a script block
    			#Important: Do not access form controls from this script block.
    			
    			Param ($Argument1) #Pass any arguments using the ArgumentList parameter
    			
    			for ($i = 0; $i -lt 10; $i++)
    			{
    				Start-Sleep -Milliseconds 1000
    				#Output Progress
    				$i + 1
    			}
    			
    			#--------------------------------------------------
    		}`
    					   -CompletedScript {
    			Param ($Job)
    			#$results = Receive-Job -Job $Job 
    			#Enable the Button
    			$buttonStartJob2.ImageIndex = -1
    			$buttonStartJob2.Enabled = $true
    		}`
    					   -UpdateScript {
    			Param ($Job)
    			#$results = Receive-Job -Job $Job -Keep
    			
    			$results2 = Receive-RSJob -Job $Job | Select-Object -Last 1
    			
    			if ($results2 -is [int])
    			{
    				
    				$progressbaroverlay2.Maximum = 10 - 1
    				$progressbaroverlay2.Value = $results2
    			}
    			
    			#Animate the Button
    			if ($null -ne $buttonStartJob2.ImageList)
    			{
    				if ($buttonStartJob2.ImageIndex -lt $buttonStartJob2.ImageList.Images.Count - 1)
    				{
    					$buttonStartJob2.ImageIndex += 1
    				}
    				else
    				{
    					$buttonStartJob2.ImageIndex = 0
    				}
    			}
    		}`
    					   -ArgumentList $null
    	}
    	
    	# --End User Generated Script--
    	#----------------------------------------------
    	#region Generated Events
    	#----------------------------------------------
    	
    	$Form_StateCorrection_Load =
    	{
    		#Correct the initial state of the form to prevent the .Net maximized form issue
    		$form1.WindowState = $InitialFormWindowState
    	}
    	
    	$Form_Cleanup_FormClosed =
    	{
    		#Remove all event handlers from the controls
    		try
    		{
    			$buttonStartJob2.remove_Click($buttonStartJob2_Click)
    			$buttonStartJob.remove_Click($buttonStartJob_Click)
    			$form1.remove_FormClosed($jobTracker_FormClosed)
    			$form1.remove_Load($form1_Load)
    			$timerJobTracker.remove_Tick($timerJobTracker_Tick)
    			$form1.remove_Load($Form_StateCorrection_Load)
    			$form1.remove_FormClosed($Form_Cleanup_FormClosed)
    		}
    		catch [Exception]
    		{ }
    	}
    	#endregion Generated Events
    	
    	#----------------------------------------------
    	#region Generated Form Code
    	#----------------------------------------------
    	$form1.SuspendLayout()
    	#
    	# form1
    	#
    	$form1.Controls.Add($progressbaroverlay2)
    	$form1.Controls.Add($buttonStartJob2)
    	$form1.Controls.Add($progressbaroverlay1)
    	$form1.Controls.Add($buttonStartJob)
    	$form1.Controls.Add($buttonOK)
    	$form1.AcceptButton = $buttonOK
    	$form1.AutoScaleDimensions = '6, 13'
    	$form1.AutoScaleMode = 'Font'
    	$form1.ClientSize = '424, 333'
    	$form1.FormBorderStyle = 'FixedDialog'
    	$form1.MaximizeBox = $False
    	$form1.MinimizeBox = $False
    	$form1.Name = 'form1'
    	$form1.StartPosition = 'CenterScreen'
    	$form1.Text = 'Form'
    	$form1.add_FormClosed($jobTracker_FormClosed)
    	$form1.add_Load($form1_Load)
    	#
    	# progressbaroverlay2
    	#
    	$progressbaroverlay2.Location = '50, 193'
    	$progressbaroverlay2.Name = 'progressbaroverlay2'
    	$progressbaroverlay2.Size = '100, 23'
    	$progressbaroverlay2.TabIndex = 2
    	#
    	# buttonStartJob2
    	#
    	$buttonStartJob2.ImageList = $imagelistButtonBusyAnimation
    	$buttonStartJob2.Location = '50, 151'
    	$buttonStartJob2.Name = 'buttonStartJob2'
    	$buttonStartJob2.Size = '75, 23'
    	$buttonStartJob2.TabIndex = 0
    	$buttonStartJob2.Text = 'Start2'
    	$buttonStartJob2.TextImageRelation = 'ImageBeforeText'
    	$buttonStartJob2.UseVisualStyleBackColor = $True
    	$buttonStartJob2.add_Click($buttonStartJob2_Click)
    	#
    	# progressbaroverlay1
    	#
    	$progressbaroverlay1.Location = '283, 193'
    	$progressbaroverlay1.Name = 'progressbaroverlay1'
    	$progressbaroverlay1.Size = '100, 23'
    	$progressbaroverlay1.TabIndex = 1
    	#
    	# buttonStartJob
    	#
    	$buttonStartJob.ImageList = $imagelistButtonBusyAnimation
    	$buttonStartJob.Location = '283, 151'
    	$buttonStartJob.Name = 'buttonStartJob'
    	$buttonStartJob.Size = '75, 23'
    	$buttonStartJob.TabIndex = 0
    	$buttonStartJob.Text = 'Start'
    	$buttonStartJob.TextImageRelation = 'ImageBeforeText'
    	$buttonStartJob.UseVisualStyleBackColor = $True
    	$buttonStartJob.add_Click($buttonStartJob_Click)
    	#
    	# buttonOK
    	#
    	$buttonOK.Anchor = 'Bottom, Right'
    	$buttonOK.DialogResult = 'OK'
    	$buttonOK.Location = '337, 298'
    	$buttonOK.Name = 'buttonOK'
    	$buttonOK.Size = '75, 23'
    	$buttonOK.TabIndex = 0
    	$buttonOK.Text = '&OK'
    	$buttonOK.UseVisualStyleBackColor = $True
    	#
    	# imagelistButtonBusyAnimation
    	#
    	$Formatter_binaryFomatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    	#region Binary Data
    	$System_IO_MemoryStream = New-Object System.IO.MemoryStream ( ,[byte[]][System.Convert]::FromBase64String('
    AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAu
    MC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAA
    ACZTeXN0ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkD
    AAAADwMAAAB2CgAAAk1TRnQBSQFMAgEBCAEAATABAAEwAQABEAEAARABAAT/ASEBAAj/AUIBTQE2
    BwABNgMAASgDAAFAAwABMAMAAQEBAAEgBgABMP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/
    AP8AugADwgH/Az0B/wM9Af8DwgH/MAADwgH/A10B/wOCAf8DwgH/sAADPQH/AwAB/wMAAf8DPQH/
    MAADggH/Az0B/wM9Af8DXQH/gAADwgH/Az0B/wM9Af8DwgH/IAADPQH/AwAB/wMAAf8DPQH/A8IB
    /wNdAf8DggH/A8IB/xAAA8IB/wM9Af8DPQH/A8IB/wNdAf8DPQH/Az0B/wNdAf8EAAOSAf8DkgH/
    A8IB/3AAAz0B/wMAAf8DAAH/Az0B/yAAA8IB/wM9Af8DPQH/A8IB/wOCAf8DPQH/Az0B/wOCAf8Q
    AAM9Af8DAAH/AwAB/wM9Af8DwgH/A10B/wOCAf8DwgH/A5IB/wOCAf8DggH/A5IB/3AAAz0B/wMA
    Af8DAAH/Az0B/zAAA10B/wM9Af8DPQH/A10B/xAAAz0B/wMAAf8DAAH/Az0B/xAAA5IB/wOSAf8D
    kgH/A8IB/3AAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wNdAf8DggH/A8IB/xAAA8IB/wM9Af8DPQH/
    A8IB/xAAA8IB/wOSAf8DkgH/A8IB/zgAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wOCAf8DXQH/A8IB
    /zAAA8IB/wPCAf8DkgH/A8IB/zQAA8IB/wPCAf80AAM9Af8DAAH/AwAB/wM9Af8wAANdAf8DPQH/
    Az0B/wNdAf8wAAOSAf8DggH/A4IB/wOSAf8wAAPCAf8DwgH/A8IB/wPCAf8wAAM9Af8DAAH/AwAB
    /wM9Af8wAAOCAf8DPQH/Az0B/wOCAf8wAAPCAf8DggH/A5IB/wOSAf8wAAPCAf8DwgH/A8IB/wPC
    Af8wAAPCAf8DPQH/Az0B/wPCAf8wAAPCAf8DggH/A10B/wPCAf8wAAPCAf8DkgH/A5IB/wPCAf80
    AAPCAf8DwgH/EAADwgH/A8IB/xQAA8IB/wOCAf8DXQH/A8IB/zAAA8IB/wOSAf8DkgH/A8IB/zQA
    A8IB/wPCAf9UAAPCAf8DwgH/A8IB/wPCAf8QAANdAf8DPQH/Az0B/wNdAf8wAAOSAf8DggH/A5IB
    /wOSAf8wAAPCAf8DwgH/A8IB/wPCAf9QAAPCAf8DwgH/A8IB/wPCAf8DwgH/A8IB/wOSAf8DwgH/
    A4IB/wM9Af8DPQH/A4IB/yQAA8IB/wPCAf8EAAPCAf8DggH/A5IB/wOSAf8wAAPCAf8DwgH/A8IB
    /wPCAf9UAAPCAf8DwgH/BAADkgH/A4IB/wOCAf8DkgH/A8IB/wOCAf8DXQH/A8IB/yAAA8IB/wPC
    Af8DwgH/A8IB/wPCAf8DkgH/A5IB/wPCAf80AAPCAf8DwgH/ZAADkgH/A5IB/wOSAf8DkgH/MAAD
    wgH/A8IB/wPCAf8DwgH/sAADwgH/A5IB/wOSAf8DwgH/NAADwgH/A8IB/7QAA8IB/wPCAf8DkgH/
    A8IB/zQAA8IB/wPCAf+0AAOSAf8DggH/A4IB/wOSAf8wAAPCAf8DwgH/A8IB/wPCAf+gAAPCAf8D
    XQH/A4IB/wPCAf8DkgH/A5IB/wOSAf8DwgH/BAADwgH/A8IB/xQAA8IB/wPCAf8DkgH/A8IB/wPC
    Af8DwgH/A8IB/wPCAf8kAAPCAf8DwgH/dAADggH/Az0B/wM9Af8DggH/A8IB/wOSAf8DkgH/A8IB
    /wPCAf8DwgH/A8IB/wPCAf8QAAOSAf8DggH/A4IB/wOSAf8EAAPCAf8DwgH/JAADwgH/A8IB/wPC
    Af8DwgH/cAADXQH/Az0B/wM9Af8DggH/EAADwgH/A8IB/wPCAf8DwgH/EAADkgH/A5IB/wOSAf8D
    kgH/MAADwgH/A8IB/wPCAf8DwgH/cAADwgH/A10B/wNdAf8DwgH/FAADwgH/A8IB/xQAA8IB/wOS
    Af8DkgH/A8IB/zQAA8IB/wPCAf9sAAPCAf8DPQH/Az0B/wPCAf8wAAPCAf8DXQH/A4IB/wPCAf8w
    AAPCAf8DwgH/A5IB/wPCAf80AAPCAf8DwgH/NAADPQH/AwAB/wMAAf8DPQH/MAADggH/Az0B/wM9
    Af8DXQH/MAADkgH/A4IB/wOCAf8DkgH/MAADwgH/A8IB/wPCAf8DwgH/MAADPQH/AwAB/wMAAf8D
    PQH/MAADXQH/Az0B/wM9Af8DggH/MAADkgH/A5IB/wOSAf8DkgH/MAADwgH/A8IB/wPCAf8DwgH/
    MAADwgH/Az0B/wM9Af8DwgH/MAADwgH/A10B/wNdAf8DwgH/MAADwgH/A5IB/wOSAf8DwgH/NAAD
    wgH/A8IB/3wAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wNdAf8DggH/A8IB/zAAA8IB/wPCAf8DkgH/
    A8IB/xAAA8IB/wM9Af8DPQH/A8IB/1AAAz0B/wMAAf8DAAH/Az0B/zAAA4IB/wM9Af8DPQH/A10B
    /zAAA5IB/wOCAf8DggH/A5IB/xAAAz0B/wMAAf8DAAH/Az0B/1AAAz0B/wMAAf8DAAH/Az0B/zAA
    A10B/wM9Af8DPQH/A4IB/wOSAf8DPQH/Az0B/wPCAf8gAAOSAf8DkgH/A5IB/wOSAf8DwgH/A10B
    /wOCAf8DwgH/Az0B/wMAAf8DAAH/Az0B/1AAA8IB/wM9Af8DPQH/A8IB/zAAA8IB/wOCAf8DXQH/
    A8IB/wM9Af8DAAH/AwAB/wM9Af8gAAPCAf8DkgH/A5IB/wPCAf8DggH/Az0B/wM9Af8DXQH/A8IB
    /wM9Af8DPQH/A8IB/6AAAz0B/wMAAf8DAAH/Az0B/zAAA10B/wM9Af8DPQH/A4IB/7AAA8IB/wM9
    Af8DPQH/A8IB/zAAA8IB/wOCAf8DXQH/A8IB/xgAAUIBTQE+BwABPgMAASgDAAFAAwABMAMAAQEB
    AAEBBQABgAEBFgAD/4EABP8B/AE/AfwBPwT/AfwBPwH8AT8D/wHDAfwBAwHAASMD/wHDAfwBAwHA
    AQMD/wHDAf8DwwP/AcMB/wPDAf8B8AH/AfAB/wHwAf8B+QH/AfAB/wHwAf8B8AH/AfAB/wHwAf8B
    8AH/AfAB/wHwAf8B8AH/AfAB/wHwAf8B+QHnAcMB/wHDAf8B5wL/AsMB/wHDAf8BwwL/AcABAwH+
    AUMB/wHDAv8B5AEDAfwBAwH/AecC/wH8AT8B/AE/BP8B/AE/Af4BfwT/AfwBPwH+AX8E/wH8AT8B
    /AE/BP8BwAEnAcABPwHnA/8BwAEDAcIBfwHDA/8DwwH/AcMD/wHDAecBwwH/AecD/wEPAf8BDwH/
    AQ8B/wGfAf8BDwH/AQ8B/wEPAf8BDwH/AQ8B/wEPAf8BDwH/AQ8B/wEPAf8BDwH/AQ8B/wGfA/8B
    wwH/AcMB/wLDAv8BwwH/AcMB/wLDAv8BwwH/AcABPwHAAQMC/wHDAf8BwAE/AcABAwT/AfwBPwH8
    AT8E/wH8AT8B/AE/Cw=='))
    	#endregion
    	$imagelistButtonBusyAnimation.ImageStream = $Formatter_binaryFomatter.Deserialize($System_IO_MemoryStream)
    	$Formatter_binaryFomatter = $null
    	$System_IO_MemoryStream = $null
    	$imagelistButtonBusyAnimation.TransparentColor = 'Transparent'
    	#
    	# timerJobTracker
    	#
    	$timerJobTracker.add_Tick($timerJobTracker_Tick)
    	$form1.ResumeLayout()
    	#endregion Generated Form Code
    	
    	#----------------------------------------------
    	
    	#Save the initial state of the form
    	$InitialFormWindowState = $form1.WindowState
    	#Init the OnLoad event to correct the initial state of the form
    	$form1.add_Load($Form_StateCorrection_Load)
    	#Clean up the control events
    	$form1.add_FormClosed($Form_Cleanup_FormClosed)
    	#Show the Form
    	return $form1.ShowDialog()
    	
    } #End Function
    
    #Call the form
    Call-powershell-job-tracker-PoshRSJob_psf | Out-Null
    

    Notes:
    You will get the below error if you use the latest version of PoshRSJob module , i had to replace the Start-RSJob.ps1 with the latest one of this location https://github.com/MVKozlov/PoshRSJob and the error is gone

    ERROR: You cannot call a method on a null-valued expression.
    Start-RSJob.ps1 (462, 70): ERROR: At Line: 462 char: 70
    ERROR: + ...     Write-Verbose "Adding Argument: $($_) "
    ERROR: +                                                  ~~~~~~~~~~~~~~~~~~~~~
    ERROR:     + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    ERROR:     + FullyQualifiedErrorId : InvokeMethodOnNull
    ERROR:
    
    

    Request-Help Needed.

    I have read about synchronized hash use in order to exchange info between runspaces .(between forms controls in my case i think)
    but it is very difficult to comprehend 🙁 at the moment !!

    Ideally i would like to provide me a working example of my code doing exactly the same using the PoshRSJob instead of Powershell jobs.The reason for this is to make comparisons and try to dig further,it would be HUGE help
    I am aware that this is advanced stuff and far beyond my level of expertise but it would be of great help to achieve my goal in an efficient manner.In the meantime i have learned a lot and getting excited about the capabilities and prospects of Powershell and runspaces.

    Epilogue

    I would like to thank everyone in advance and sorry for this large post

  • #82321

    Don Jones
    Keymaster

    O-Kay. That is a LOT of code, and you have jumped into the 20 foot deep section of the pool without learning to doggie paddle ;). You are literally doing hardcore .NET Framework programming, here, not "PowerShell scripting" – just be aware of that.

    I'm personally not going to be of any help; I love working with newcomers to PowerShell (it's my schtick and I've written books on it), but you start getting into thread sync and stuff and you're just outside the realm of PowerShell proper. I mention it because most folks I've seen posting on these forums aren't doing this stuff – you might find a better answer, or at least more of a clue, on a developer-oriented site like StackOverflow.com (I just don't want you hanging around waiting on an answer that doesn't come – you're still very welcome here). I mean, I get that you're using PowerShell, but you're really doing .NET programming, and it might be a lot easier and more efficient in C# to be honest.

    Something to bear in mind is that PowerShell is a single-threaded environment, and while you can do some tricks to make it seem multi-threaded, many of those tricks end up being multi-PROCESS, which makes it harder to marshal data back and forth. Making multi-tasking GUIs (and updating a progress bar "counts" as multi-tasked) just isn't PowerShell's primary (or secondary) use case, so you definitely do start getting into weird, "edge" situations pretty quickly.

    Are you certain PowerShell is the best tool for the job? I mean, I can build a house with a hammer, but I'd much rather have a nail gun.

  • #82324

    Will Prather
    Participant

    I'll be honest, I haven't read your code. However, I have been doing some small GUI's in PowerShell recently, with multiple runspaces to do things like progress bars and detailed logging sections. Stephen Owen has a really good post leveraging some work the Boe Prox has done, https://foxdeploy.com/series/learning-gui-toolmaking-series/.

    It talks very specifically about how to make your app responsive so you don't get a hung GUI because you clicked a button. You'll have to understand that, and then you can migrate some of your code into their example code.

    To Don's point, maybe there are better tools for the job. Like I said, I've made some GUI's in PowerShell with multiple runspaces. I could do PowerShell, but I couldn't do C# effectively; that was the primary reason.

  • #82327

    Linus
    Participant

    Thanks Don for this lightning fast and thorough answer.

    I cannot disagree that you have some valid arguments here but the reasoning for these questions is that eventually i have to create a GUI that provides the capability to do many maintenance tasks-custom operations for many windows servers from a central point . I think powershell is the king for windows administration so this is why i chose that route.In addition the fact that i am not a developer makes it easier to comprehend a "scripting" language so going for "real" programming language will be very,very difficult. !! for me personally

    I tried to find a solution myself but eventually i reached a dead-end and asked for help here in order to proceed.

    Really appreciate your prompt answer although i have to state i just begun !! 🙂

    Thank you again

  • #82328

    Linus
    Participant

    Thanks Will for your answer and the provide link!!

    I have found this info in the past but my main problem is the lack of knowledge so i cannot really judge which route should i go.
    I took the route to use the PoshRSJob module that in "someway" replicates the operation of background jobs with runspaces for easier understanding.If i have to use synchronized hash and runspaces in the bare format maybe i will have to stick only to background jobs and forget about runspaces.

    I know i am over my head here but would hate to devote thousand of hours creating this gui that does not scale and then only figuring out in the end that i have to rewrite everything from scratch.If i have to wait just one minute per server this means 2 whole hours for 120 servers.It is obvious that going with simple foreach will fail eventually.

    In any case will look once more the link provided and hopefully will get somewhere

    Thank you again

  • #82376

    Wei-Yen Tan
    Participant

    On a sidenote, If I was to create a GUI I would be looking at two products depending on what your tastes are to do the gui heavy lifting so you can focus on the code. Powershell Studio is my preferred option but you have to pay for that, and second is PowerGui.

  • #82378

    Linus
    Participant

    Thanks for your recommendations Wei
    The code may seem that it a LOT of code but it is just a gui containing two "special" buttons that support background jobs which are embedded in powershell studio.I haven't checked powerGUI, i will give it a try.
    My main question is if i could replicate the operation of background jobs in the first exhibit of code by using the PoshRSJob module in order to utilize runspaces

  • #82399

    Rick
    Participant

    I've dealt with similar issues with some of the scripts/utilities I've created. With the number of remote computers growing psjobs is never going to keep up. I would suggest batching each task into a separate scheduled task, (ie, check disk space, kill process, etc) and scheduling it on the remote computer a minute or 2 after you set it up. Have the scheduled task write the results to the local event log of the remote computer and also the event log of the computer your running your script from so you can read the results. This way it offloads all the work from your single computer to the endpoints and centralizes logging to the event logs for easy reading. Your progress bar would then just be displaying the progress setting up each scheduled task and should be very fast. Don't forget to set expire and delete times on the remote scheduled task so the scheduled tasks are temporary don't remain on the remote computers.

    Good luck!

  • #82453

    Linus
    Participant

    Hi Rick

    Thank you very much for your recommendations.

    You have given another perspective on how i should handle this situation.The reason for searching implementing the PoshRSJob module was that it provides throttling and it is faster than psjobs,also regarding logging i was looking in the mutex area but again you have provided another way.The program should also push specific files to remote computers and also pull specific files so for sure some operation will take time (pull files from remote servers) will freeze the gui.I am a complete newbie so 99,9% i am mistaken but i think the scheduled task way is a bit of a workaround and not let's say best practice.
    I really appreciate the time you spent trying to give a solution in my problem.The truth is i have not even thought going this route.

    For sure i will look into it and test a bit to see how it works in my specific case.
    Thank you again

You must be logged in to reply to this topic.