I've cobbled together an example of a runspace popping up a Windows form and a timer closing it after 3 seconds. See this example:
$runspace = [runspacefactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions = "ReuseThread"
$runspace.Open()
$PowerShell = [PowerShell]::Create().AddScript({
$objForm = New-Object System.Windows.Forms.Form
$Timer = New-Object System.Windows.Forms.Timer
$Timer.Interval = 1000
$script:countdown = 3
$Timer.Add_Tick({
--$script:countdown
if ($script:countdown -lt 0)
{
$Timer.Dispose();
$objForm.Dispose();
}
})
$Timer.Start()
$objForm.ShowDialog() | Out-Null
})
$PowerShell.Runspace = $runspace
$null = Register-ObjectEvent -InputObject $PowerShell -EventName InvocationStateChanged -Action {
param([System.Management.Automation.PowerShell] $ps)
$state = $EventArgs.InvocationStateInfo.State
if ($state -in 'Completed', 'Failed', 'Stopped') {
write-host $state
$ps.Runspace.Dispose()
[GC]::Collect()
}
}
$AsyncHandle = $PowerShell.BeginInvoke()
$PowerShell.EndInvoke($AsyncHandle)
$PowerShell.Dispose()
write-host "Continue with the rest of my script"
The idea is to open the form in a separate runspace and continue with my script processing. However, it doesn't continue the script unless i remove the lines:
$PowerShell.EndInvoke($AsyncHandle)
$PowerShell.Dispose()
But if I remove these lines, the script will currently leave a handle open to the runspace.
My question is, can I auto-cleanup the runspace when the runspace is complete somewhere here?
if ($state -in 'Completed', 'Failed', 'Stopped') {
#call EndInvoke on this instance??
$ps.Runspace.Dispose()
[GC]::Collect()
}
Thanks.
UPDATE - Attempt 1
$runspace = [runspacefactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions = "ReuseThread"
$runspace.Open()
$PowerShell = [PowerShell]::Create().AddScript({
$objForm = New-Object System.Windows.Forms.Form
$Timer = New-Object System.Windows.Forms.Timer
$Timer.Interval = 1000
$script:countdown = 3
$Timer.Add_Tick({
--$script:countdown
if ($script:countdown -lt 0)
{
$Timer.Dispose();
$objForm.Dispose();
}
})
$Timer.Start()
$objForm.ShowDialog() | Out-Null
})
$PowerShell.Runspace = $runspace
$null = Register-ObjectEvent -InputObject $PowerShell -EventName InvocationStateChanged -Action {
param([System.Management.Automation.PowerShell] $ps)
if($EventArgs.InvocationStateInfo.State -in 'Completed', 'Failed', 'Stopped') {
$state['Instance'].EndInvoke($state['Handle'])
$state['Instance'].Runspace.Dispose()
$state['Instance'].Dispose()
}
}
$state = @{
Instance = $PowerShell
Handle = $PowerShell.BeginInvoke()
}
write-host "Continue with the rest of my script"
$state['Instance'].Runspace.RunspaceStateInfo.State
start-sleep -Seconds 10
$state['Instance'].Runspace.RunspaceStateInfo.State
UPDATE - Attempt 2
function Show-Form() {
$runspace = [runspacefactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions = "ReuseThread"
$runspace.Open()
$PowerShell = [PowerShell]::Create().AddScript({
$objForm = New-Object System.Windows.Forms.Form
$Timer = New-Object System.Windows.Forms.Timer
$Timer.Interval = 1000
$script:countdown = 3
$Timer.Add_Tick({
--$script:countdown
if ($script:countdown -lt 0)
{
$Timer.Dispose();
$objForm.Dispose();
}
})
$Timer.Start()
$objForm.ShowDialog() | Out-Null
})
$PowerShell.Runspace = $runspace
$null = Register-ObjectEvent -InputObject $PowerShell -EventName InvocationStateChanged -Action {
param([System.Management.Automation.PowerShell] $ps)
if($EventArgs.InvocationStateInfo.State -in 'Completed', 'Failed', 'Stopped') {
$global:state['Instance'].EndInvoke($global:state['Handle'])
$global:state['Instance'].Runspace.Dispose()
$global:state['Instance'].Dispose()
}
}
$global:state = @{
Instance = $PowerShell
Handle = $PowerShell.BeginInvoke()
}
}
Show-Form
#do other stuff
do {
#write-host
$global:state['Instance'].Runspace.RunspaceStateInfo.State
start-sleep 1
} while($global:state['Instance'].Runspace.RunspaceStateInfo.State -ne 'closed')
Attempt 3
function Show-Form() {
$runspace = [runspacefactory]::CreateRunspace()
$runspace.ApartmentState = "STA"
$runspace.ThreadOptions = "UseNewThread"
$runspace.Open()
$PowerShell = [PowerShell]::Create().AddScript({
$objForm = New-Object System.Windows.Forms.Form
$objForm.ShowDialog() | Out-Null
})
$PowerShell.Runspace = $runspace
$state = @{
Instance = $PowerShell
Handle = $PowerShell.BeginInvoke()
}
$Jobs.Add($state) | Out-Null
$null = Register-ObjectEvent -InputObject $state.Instance -MessageData $state.Handle -EventName InvocationStateChanged -Action {
param([System.Management.Automation.PowerShell] $ps)
if($ps.InvocationStateInfo.State -in 'Completed', 'Failed', 'Stopped') {
write-host "closing instance " $ps
write-host "closing handle " $Event.MessageData
$ps.Runspace.Close()
$ps.Runspace.Dispose()
$ps.EndInvoke($Event.MessageData)
$ps.Dispose()
[GC]::Collect()
}
write-host "finish"
}
}
Show-Form
Show-Form
write-host "other stuff"