I've started thinkering around with runspaces in powershell and I was able to do the followings
- Create the RunSpace
- Pass variables to it
- Pass a pre-written function to it
- GUI/WPF shows up and I can even manipulate the elements on it (Eg. changing text within the textblock)
The issue is one of the buttons (BTN_Exit)...
I would like to achieve that when clicked, it's not only closing the GUI but closes the RunSpace where it runs. I've got to the point where I managed to close the GUI and even to call the close sequence for the Runspace but (I assume) as the called function runs in the RunSpace itself, it just hangs up (RunSpace remains in "closing" state) -Apparently Powershell is unable to kill itself :) Luckily I am still able to dispose it from the main thread with $(Get-Runspace)[-1].Dispose()
(Manually)
I believe I need to connect back to the main thread where the GUI Runspace has been created and close it from there but I am unable to get back there within the Function. Manually if window is open I am able to execute all cmdlets within the close-runspace function and it achieves the desired goal.
I've tried adding $rs.connect($(Get-Runspace).Name -eq "Runspace1")
& $RS.disconnect()
If I try the same thing on the "Handle" or Instance it yields the same result. Neither was I able to call the original function on button click without passing the function to the RunSpace.
How would I get programmatically to the point that when the Button is being clicked it closes the GUI and disposes the Runspace?
Here is the code:
#===[___VARIABLES___]===
$GUI = [hashtable]::Synchronized(@{})
$GUI.Host = $host
#===[___FUNCTIONS___]===
function Close-RunSpace {
if(!($RSI01H.Iscompleted)){
$GUI.BS.Dispatcher.invoke([action]{
$GUI.BS.Close()
})
}
$RSI01.EndInvoke($RSI01H)
$RS.Close()
$RS.Dispose()
}
#===[__RunSpaceCfg__]===
$RS_ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$RS_ISS.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'Close-RunSpace', (Get-Content Function:\Close-RunSpace -ErrorAction Stop)))
$RS = [runspacefactory]::CreateRunspace($RS_ISS)
$RS.ApartmentState = "STA"
$RS.ThreadOptions = "ReuseThread"
$RS.Open()
$RS.SessionStateProxy.SetVariable("GUI",$GUI)
$RS.SessionStateProxy.SetVariable("RS",$RS)
$RS.SessionStateProxy.SetVariable("RSI01",$RSI01)
$RS.SessionStateProxy.SetVariable("RSI01H",$RSI01H)
#===[___EXECUTION___]===
$RSI01 = [powershell]::Create().AddScript(
{
[void][System.Reflection.Assembly]::LoadWithPartialName("PresentationCore")
[void][System.Reflection.Assembly]::LoadWithPartialName("PresentationFramework")
[void][System.Reflection.Assembly]::LoadWithPartialName("WindowsBase")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Def_XAML = DATA {'
<Window x:Name="BS" x:Class="PST.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PST"
mc:Ignorable="d"
Title="BootStrapR"
Height="450"
Width="800"
ResizeMode="NoResize"
Topmost="True"
FontFamily="Arial"
WindowStartupLocation="CenterScreen"
WindowStyle="None">
<Grid>
<Button x:Name="BTN_Update"
Content="Update"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="109,352,0,0"
Width="75"/>
<Button x:Name="BTN_Exit"
Content="exit"
HorizontalAlignment="Left"
Margin="431,352,0,0"
VerticalAlignment="Top"
Width="75"/>
<ProgressBar x:Name="pb"
HorizontalAlignment="Left"
Height="29"
Margin="10,150,0,0"
VerticalAlignment="Top"
Width="780"
Background="White"
Foreground="Black"
BorderBrush="White"
Value="0" />
<TextBlock x:Name="pstext"
HorizontalAlignment="Left"
Height="266"
Margin="26,22,0,0"
TextWrapping="Wrap"
Text="TextBlock"
VerticalAlignment="Top"
Width="223"/>
</Grid>
</Window>
'}
[xml]$XAML = $Def_XAML -replace 'x:Class=*.*','' `
-replace 'mc:Ignorable="d"','' `
-replace "x:Name",'Name'
$WPF = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
$XAML.SelectNodes("//*[@Name]") | ForEach-Object{
$GUI."$($_.Name)" = $WPF.FindName($_.Name)
}
$GUI.Error = $Error
$GUI.BTN_Exit.Add_Click({
#$GUI.BS.Close()
Close-RunSpace
})
$GUI.BTN_Update.Add_Click({
$GUI.pb.Value+=1
})
$GUI.BS.ShowDialog() | Out-Null
}
)
$RSI01.Runspace = $RS
$RSI01.Runspace.Name = "GUI"
$RSI01H = $RSI01.BeginInvoke()