Background jobs not processing successfully

Ask questions about creating Graphical User Interfaces (GUI) in PowerShell and using WinForms controls.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Locked
User avatar
whiggs2
Posts: 38
Joined: Sat Sep 23, 2017 11:59 pm

Background jobs not processing successfully

Post by whiggs2 » Tue May 08, 2018 1:43 pm

To help you better we need some information from you.

*** Please fill in the fields below. If you leave fields empty or specify 'latest' rather than the actual version your answer will be delayed as we will be forced to ask you for this information. ***

Product, version and build: Powershell Studio 2018 Version 5.5.152
32 or 64 bit version of product: 64 bit
Operating system:
Microsoft Windows 10 Enterprise
10.0.16299 N/A Build 16299
32 or 64 bit OS: 64 bit

Ok. I am at a loss. I have been writing a powershell script with a GUI, and am using background jobs to allow the form to be more responsive, using the functions obtained from the below sapien post:
https://www.sapien.com/blog/2012/05/16/ ... ive-forms/
I have been able to successfully implement this functionality in the past with no issue, however, fairly recently (I am pretty sure it started after the last powershell studio update), either when I am running the deployed source script that I run directly from powershell or a compiled executable, the background jobs just hang and never complete. However, the previous scripts using the same background job functions still process successfully and if I take the background job that the script is hanging on and create the job myself manually (with start-job, receive-job, etc), then it runs successfully. I can only think that this is due to some change in the way that powershell Studio compiles the scripts. The portion that is hanging is form_load event, and while it appears that it processes all of the commands in the jobscript script block, it the content in the "completescript" block never process. Below is the code in question:

Code: Select all

$MainForm_Load = {
	
	#TODO: Initialize Form Controls here
	$progressbaroverlay1.Visible = $true
	Add-JobTracker -Name Startup -JobScript {
		param ($scriptdir,
			$scriptnamee
		)
		$sec = ConvertTo-SecureString 'stuff' -AsPlainText -Force
		$necred = New-Object System.Management.Automation.PSCredential -ArgumentList "stuff", $sec
		Get-PackageProvider -Name NuGet -ForceBootstrap -Force
		Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
		iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
		import-module "C:\ProgramData\chocolatey\helpers\chocolateyInstaller.psm1" -Force
		Update-SessionEnvironment
		#install the latest version of the departmodule from the VSTS package feed.
		choco upgrade NuGet.commandline -y
		Update-SessionEnvironment
		NuGet update -self
		Install-Module Powershellget -Repository PSGallery -RequiredVersion 1.5.0.0 -Force -AllowClobber
		Remove-Module powershellget -Force
		Import-Module powershellget -RequiredVersion 1.5.0.0 -Force
		If (!(Test-Path "$env:ProgramFiles\WindowsPowerShell\Modules\PoshInternals"))
		{
			Install-Module PoshInternals -AllowClobber -Force -Repository "PSGallery"
		}
		Else
		{
			Update-Module PoshInternals -Force
		}
		If (!(Test-Path "$env:ProgramFiles\WindowsPowerShell\Modules\AzureRM.KeyVault"))
		{
			Install-Module AzureRm -AllowClobber -Force -Repository "PSGallery"
		}
		else
		{
			Update-Module Azurerm -Force
		}
		If (!(Test-Path $env:ProgramFiles\WindowsPowerShell\Modules\SharePointPnPPowerShellOnline))
		{
			Install-Module SharePointPnPPowerShellOnline -AllowClobber -Force -Repository "PSGallery"
		}
		Else
		{
			Update-Module SharePointPnPPowerShell* -Force
		}
		If (!(Test-Path $env:ProgramFiles\WindowsPowerShell\Modules\MSOnline))
		{
			Install-Module MSOnline -AllowClobber -Force -Repository "PSGallery"
		}
		Else
		{
			Update-Module MSOnline -Force
		}
		$repsource = Get-PackageSource -PackageManagementProvider Nuget -ProviderName PowerShellGet -Force -ForceBootstrap
		If ($repsource.Name -notcontains "EE")
		{
			NuGet Sources Add -Name "EE" -Source "stuff/_packaging/EE/nuget/v2/" -Username "usertoken" -Password 'pippgvuy2txlc3aebry7ke77jqjd3woing7rte5homsaj2iy7lpq' -storePasswordInClearText
		}
		$psrep = Get-PSRepository -Name "EE" -ErrorAction SilentlyContinue
		if ($psrep -eq $null)
		{
			Register-PSRepository -Name "EE" -SourceLocation "stuff/_packaging/EE/nuget/v2/" -Credential $necred -InstallationPolicy Trusted
		}
		$instmodule = $null
		$repmodule = $null
		#test to see if the departmodule is already present on the system.  If it is,
		#uninstall it.
		If (Test-Path $env:ProgramFiles\WindowsPowerShell\Modules\DepartModule)
		{
			$instmodule = Get-InstalledModule -Name Departmodule
			$repmodule = Find-Module -Name DepartModule -Repository EE -Credential $necred
			If ([System.Version]$repmodule.Version -gt [System.Version]$instmodule.Version)
			{
				#Get-Handle -Name "$env:ProgramFiles\WindowsPowerShell\Modules\DepartModule" | Close-Handle -Confirm:$false -ErrorAction SilentlyContinue
				Get-Handle -Name "$env:ProgramFiles\WindowsPowerShell\Modules\DepartModule\*" | Close-Handle -Confirm:$false -ErrorAction SilentlyContinue
				Uninstall-Module Departmodule -Force -AllVersions
				Install-Module DepartModule -Repository "EE" -Force -AllowClobber -Credential $necred
			}
		}
		Else
		{
			Install-Module DepartModule -Repository "EE" -Force -AllowClobber -Credential $necred
		}
		
		
		Import-Module DepartModule -Force
		#test to see if the Remote server administration tools are installed on the system.
		#If they are not, install them.
		Try
		{
			Import-Module Activedirectory -Force -ErrorAction Stop
		}
		Catch
		{
			$wmi = Get-WmiObject -Class Win32_OperatingSystem -Namespace Root\cimv2 -Property "caption"
			If ($wmi.caption -like "*server*")
			{
				Add-WindowsFeature -Name RSAT-AD-Tools
				Add-windowsFeature -Name RSAT-AD-PowerShell
				Import-Module ActiveDirectory
			}
			Else
			{
				Install-RSATTools
				Import-Module ActiveDirectory
			}
		}
		choco upgrade serviceassistant --source https://www.myget.org/F/serviceassistant/api/v2 -y
		choco upgrade sharepointconsole --source https://www.myget.org/F/sharepointconsole/api/v2 -y
		Import-Module "$env:ProgramFiles\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell" -Force
		$secstt = ConvertTo-SecureString "stuff" -asplaintext -force
		$creds = New-Object System.Management.Automation.PSCredential -ArgumentList "stuff"
		Login-AzureRmAccount -Credential $creds
		$clientid = $null
		$clientsecret = $null
		$passphrase = $null
		$privatekey = $null
		$publickeyid = $null
		$enterpriseid = $null
		$clientid2 = $null
		$clientsecret2 = $null
		$passphrase2 = $null
		$privatekey2 = $null
		$publickeyid2 = $null
		$enterpriseid2 = $null
		$secrets = Get-AzureKeyVaultSecret -VaultName boxauth
		Foreach ($item in $secrets)
		{
			$azkey = Get-AzureKeyVaultSecret -name $item.Name -VaultName $item.VaultName
			If ($azkey.Name -like "clientID")
			{
				$clientid = $azkey.SecretValueText
			}
			ElseIf ($azkey.Name -like "clientSecret")
			{
				$clientsecret = $azkey.SecretValueText
			}
			Elseif ($azkey.Name -like "passphrase")
			{
				$passphrase = $azkey.SecretValueText
			}
			ElseIf ($azkey.Name -like "privateKey")
			{
				$privatekey = $azkey.SecretValueText #.replace("\n","`n").ToString()
			}
			Elseif ($azkey.Name -like "publicKeyID")
			{
				$publickeyid = $azkey.SecretValueText
			}
			Elseif ($azkey.Name -like "enterpriseID")
			{
				$enterpriseid = $azkey.SecretValueText
			}
			If ($azkey.Name -like "clientID2")
			{
				$clientid2 = $azkey.SecretValueText
			}
			ElseIf ($azkey.Name -like "clientSecret2")
			{
				$clientsecret2 = $azkey.SecretValueText
			}
			Elseif ($azkey.Name -like "passphrase2")
			{
				$passphrase2 = $azkey.SecretValueText
			}
			ElseIf ($azkey.Name -like "privateKey2")
			{
				$privatekey2 = $azkey.SecretValueText #.replace("\n","`n").ToString()
			}
			Elseif ($azkey.Name -like "publicKeyID2")
			{
				$publickeyid2 = $azkey.SecretValueText
			}
			Elseif ($azkey.Name -like "enterpriseID2")
			{
				$enterpriseid2 = $azkey.SecretValueText
			}
			Else
			{
			}
		}
		
		$boxconfig = New-Object -TypeName Box.V2.Config.Boxconfig($clientid, $clientSecret, $enterpriseID, $privateKey, $passphrase, $publicKeyID)
		$boxJWT = New-Object -TypeName Box.V2.JWTAuth.BoxJWTAuth($boxconfig)
		$boxjwt
		$tokenreal = $boxJWT.AdminToken()
		$adminclient = $boxjwt.AdminClient($tokenreal, "401268528")
		$adminclient
		
		$boxconfig2 = New-Object -TypeName Box.V2.Config.Boxconfig($clientid2, $clientSecret2, $enterpriseID2, $privateKey2, $passphrase2, $publicKeyID2)
		$boxJWT2 = New-Object -TypeName Box.V2.JWTAuth.BoxJWTAuth($boxconfig2)
		$boxjwt2
		$tokenreal2 = $boxJWT2.AdminToken()
		$adminclient2 = $boxjwt2.AdminClient($tokenreal2, "401268528")
		$adminclient2
		$number = $adminclient.UsersManager.GetEnterpriseUsersAsync($null, 0, 1000)
		$number.Wait()
		$number.Result.TotalCount
	} -CompletedScript {
		param ($job)
		$results = Receive-Job -Job $job | Select-Object -Last 1
		If ($results -is [int])
		{
			$labelaccount.Text = "$results of 2250 licenses used"
			If (2250 - [Int]$results -lt 10)
			{
				$labelaccount.ForeColor = 'Red'
			}
			Else
			{
				$labelaccount.ForeColor = 'Black'
			}
		}
		
		Import-Module DepartModule -Force
		
		$progressbaroverlay1.Visible = $false
		$labelaccount.Visible = $true
		$textbox1.Enabled = $true
		$textbox2.Enabled = $true
		$listview1.Enabled = $true
		$button1.Enabled = $true
		$buttonSearch.Enabled = $false
		$buttonAssignLicense.Enabled = $false
		$buttonRemoveBoxLicense.Enabled = $false
	} -ArgumentList $ScriptDirectory, $scriptname
}
DO NOT POST SUBSCRIPTIONS, KEYS OR ANY OTHER LICENSING INFORMATION IN THIS FORUM

User avatar
davidc
Posts: 5913
Joined: Thu Aug 18, 2011 4:56 am

Re: Background jobs not processing successfully

Post by davidc » Tue May 08, 2018 2:40 pm

[TOPIC MOVED TO POWERSHELL GUIS FORUM BY MODERATOR]

1. Check that the script is running with Admin rights since you are calling Install-Module.
2. Check the status of the job. Maybe there is an error in the job script.
3. Are you using the code from the website or the Job Track control set? Make sure you use the control set that comes with PowerShell Studio.
David
SAPIEN Technologies, Inc.

User avatar
whiggs2
Posts: 38
Joined: Sat Sep 23, 2017 11:59 pm

Re: Background jobs not processing successfully

Post by whiggs2 » Wed May 09, 2018 8:29 am

1. The script is running as admin (as I run it after packaging it as an exe with a manifest for elevation.
2. As I stated in the post, if I take the same snippette of code, place it in a script block, and then run that script block as a background job, it executes without error.
3. I am using the code from the website (these functions are defined in the globals.ps1 tab):

Code: Select all

$JobTrackerList = New-Object System.Collections.ArrayList
function Add-JobTracker
{
		<#
			.SYNOPSIS
				Add a new job to the JobTracker and starts the timer.
		
			.DESCRIPTION
				Add a new job to the JobTracker and starts the timer.
		
			.PARAMETER  Name
				The name to assign to the Job
		
			.PARAMETER  JobScript
				The script block that the Job will be performing. 
				Important: Do not access form controls from this script block.
		
			.PARAMETER ArgumentList
				The arguments to pass to the job
		
			.PARAMETER  CompleteScript
				The script block that will be called when the job is complete.
				The job is passed as an argument. The Job argument is null when the job fails.
		
			.PARAMETER  UpdateScript
				The script block that will be called each time the timer ticks. 
				The job is passed as an argument. Use this to get the Job's progress.
		
			.EXAMPLE
				Job-Begin -Name "JobName" `
				-JobScript {	
					#Important: Do not access form controls from this script block.
					Get-WmiObject Win32_Process -Namespace "root\CIMV2"
				}`
				-CompletedScript {
					Param($Job)		
					$results = Receive-Job -Job $Job		
				}`
				-UpdateScript {
					Param($Job)
					#$results = Receive-Job -Job $Job -Keep
				}
		
			.LINK
				
		#>
	
	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 ($job -ne $null)
	{
		#Create a Custom Object to keep track of the Job & Script Blocks
		$psObject = New-Object System.Management.Automation.PSObject
		
		Add-Member -InputObject $psObject -MemberType 'NoteProperty' -Name Job -Value $job
		Add-Member -InputObject $psObject -MemberType 'NoteProperty' -Name CompleteScript -Value $CompletedScript
		Add-Member -InputObject $psObject -MemberType 'NoteProperty' -Name UpdateScript -Value $UpdateScript
		
		[void]$JobTrackerList.Add($psObject)
		
		#Start the Timer
		if (-not $timerJobTracker.Enabled)
		{
			$timerJobTracker.Start()
		}
	}
	elseif ($CompletedScript -ne $null)
	{
		#Failed
		Invoke-Command -ScriptBlock $CompletedScript -ArgumentList $null
	}
	
}

function Update-JobTracker
{
		<#
			.SYNOPSIS
				Checks the status of each job on the list.
		#>
	
	#Poll the jobs for status updates
	$timerJobTracker.Stop() #Freeze the Timer
	
	for ($index = 0; $index -lt $JobTrackerList.Count; $index++)
	{
		$psObject = $JobTrackerList[$index]
		
		if ($psObject -ne $null)
		{
			if ($psObject.Job -ne $null)
			{
				if ($psObject.Job.State -ne "Running")
				{
					#Call the Complete Script Block
					if ($psObject.CompleteScript -ne $null)
					{
						#$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 ($psObject.UpdateScript -ne $null)
				{
					#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
{
		<#
			.SYNOPSIS
				Stops and removes all Jobs from the list.
		#>
	#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
I have used this code for numerous other jobs successfully, so I am unsure as to why it would fail now. Also, I am not familiar with how to use/implement a job tracker control request, but it sounds like a interesting feature that might help me resolve my issue. Any good documentation I can look at for a good explanation?

User avatar
davidc
Posts: 5913
Joined: Thu Aug 18, 2011 4:56 am

Re: Background jobs not processing successfully

Post by davidc » Wed May 09, 2018 9:25 am

It is really hard to tell what is happening without seeing the code. If you can, please zip and upload the project here and I will take a look at it:

https://www.sapien.com/support/upload
David
SAPIEN Technologies, Inc.

Locked