Answered by:
WPF Runspaces and shared variables

Question
-
Hi,
i already asked the question here
i have a problem with shared variables in runspaces with wpf forms.
I habe a wpf form with a button and a listbox. On the listbox i have a menuitem to export the results from the listbox.
When i click on the button i start a runspace, that gets some information (in the example i just do a get-service) and writes them to the listbox.
To have the possiblity to do an export of the items in the list box with all of the attributes i want to write the results of the command (get-service) to a shared variable that i can use in the wpf form later.
How it should work:- Click Button
--> Start Runspace and call get-service (Search Button and Export-csv will be set to isenabled=false)
--> Write the results to a shared variable
--> Add the results to the listbox within the runspace
--> When runspace finished set search Button and Export-csv to isenabled=true
- Click ExportCSV
--> export the results from the shared variable with all attributesWithin the runspace i can see that the variable is filled correctly and i can add the results to the listbox or a text field in the wpf gui. The problem is, that when i try to use the shared variable outside the runspace it is empty.
If i do a similar thing without wpf gui the sharedvariable is accesible from my powershell session but wpf it wont work.
I am really stuck here and don't get a way to manage it. Can you please provide some help/ideas how to do it.
The global variable handle just was for testing issues.
Thanks in advance.
Regards,
Tom
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") #region Load XAML [xml]$script:XAMLTest = @" <Window x:Class="Test.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:Test" mc:Ignorable="d" Title="Test" Height="479.303" Width="320.697" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Background="White"> <Grid Margin="0,1,2,-1" Background="#FFF0F0F0"> <Button x:Name="btn_search" Content="Search" HorizontalAlignment="Left" Margin="111,204,0,0" VerticalAlignment="Top" Width="75" FontSize="12" RenderTransformOrigin="3.108,9.031"/> <ListBox x:Name="lst_search" HorizontalAlignment="Left" Height="189" Margin="27,10,0,0" VerticalAlignment="Top" Width="269"> <ListBox.ContextMenu> <ContextMenu AllowDrop="True"> <MenuItem x:Name="lmi_exportcsv" Header="Export to CSV" IsEnabled="True"/> </ContextMenu> </ListBox.ContextMenu> </ListBox> <TextBox x:Name="txt_message" HorizontalAlignment="Left" Height="155" Margin="10,270,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="286" IsReadOnly="True" Background="#FFF0F0F0" VerticalScrollBarVisibility="Auto"/> </Grid> </Window> "@ -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window' #region Load Form try { $TestForm = [Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $XAMLTest) ) } catch { Write-Host "Cannot load Windows.Markup.XamlReader" } $XAMLTest.SelectNodes("//*[@Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $TestForm.FindName($_.Name)} #endregion Load Form #region variables $services = @() $script:sharedData = [HashTable]::Synchronized(@{}) #endregion variables #region functions function Invoke-ServiceSearch{ [CmdletBinding()] param ( [parameter(Position=0, Mandatory=$false)] $Testform, [parameter(Position=1, Mandatory=$false)] $lst_search, [parameter(Position=2, Mandatory=$false)] $btn_search, [parameter(Position=3, Mandatory=$false)] $txt_message, [parameter(Position=4, Mandatory=$false)] $lmi_exportcsv, [parameter(Position=5, Mandatory=$false)] $services ) $code = { $shareddata.services = Get-Service $sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.lst_search.ItemsSource = $sharedData.services},'Normal') $sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.txt_message.AppendText("Runspace: $($Shareddata.services.Name)")}) #$sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.lmi_exportcsv.IsEnabled="True"}) } #region adding variables for use in new runspace $shareddata = @{ testform = $testform lst_search = $lst_search btn_search = $btn_search txt_message = $txt_message services = $services error = $error } #endregion adding variables #generate a new runspace $runspace = [RunSpaceFactory]::CreateRunspace() $runspace.ApartmentState = 'STA' $runspace.ThreadOptions = 'ReuseThread' $runspace.Open() #add shared variable to runspace $runspace.SessionStateProxy.setVariable("sharedData", $sharedData) #generate a new psthread and add script code $ps = [PowerShell]::Create().AddScript($code) $ps.Runspace = $runspace $global:handle = $ps.BeginInvoke() } #endregion functions $btn_search.Add_Click{ $SearchParams = @{ testform = $TestForm btn_search = $btn_search lst_search = $lst_search txt_message = $txt_message lmi_exportcsv = $lmi_exportcsv services = $services } Invoke-ServiceSearch @SearchParams } $lmi_exportcsv.Add_Click{ $txt_message.AppendText("`nMain: $($shareddata.services.Name)") } $null = $TestForm.ShowDialog()
Thursday, October 17, 2019 6:02 AM
Answers
-
#region functions function Invoke-ServiceSearch{ [CmdletBinding()] param ( $Testform, $lst_search, $btn_search, $txt_message, $lmi_exportcsv, $services ) $code = { $shareddata.services = Get-Service $sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.lst_search.ItemsSource = $sharedData.services},'Normal') $sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.txt_message.AppendText("Runspace: $($Shareddata.services.Name)")}) #$sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.lmi_exportcsv.IsEnabled="True"}) } $script:sharedData = [HashTable]::Synchronized(@{ testform = $testform lst_search = $lst_search btn_search = $btn_search txt_message = $txt_message services = $services error = $error } )
#generate a new runspace $runspace = [RunSpaceFactory]::CreateRunspace() $runspace.ApartmentState = 'STA' $runspace.ThreadOptions = 'ReuseThread' $runspace.Open() #add shared variable to runspace $runspace.SessionStateProxy.setVariable("sharedData", $sharedData) #generate a new psthread and add script code $ps = [PowerShell]::Create().AddScript($code) $ps.Runspace = $runspace $global:handle = $ps.BeginInvoke() } #endregion functions #region Load XAML [xml]$script:XAMLTest = @" <Window x:Class="Test.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:Test" mc:Ignorable="d" Title="Test" Height="479.303" Width="320.697" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Background="White"> <Grid Margin="0,1,2,-1" Background="#FFF0F0F0"> <Button x:Name="btn_search" Content="Search" HorizontalAlignment="Left" Margin="111,204,0,0" VerticalAlignment="Top" Width="75" FontSize="12" RenderTransformOrigin="3.108,9.031"/> <ListBox x:Name="lst_search" HorizontalAlignment="Left" Height="189" Margin="27,10,0,0" VerticalAlignment="Top" Width="269"> <ListBox.ContextMenu> <ContextMenu AllowDrop="True"> <MenuItem x:Name="lmi_exportcsv" Header="Export to CSV" IsEnabled="True"/> </ContextMenu> </ListBox.ContextMenu> </ListBox> <TextBox x:Name="txt_message" HorizontalAlignment="Left" Height="155" Margin="10,270,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="286" IsReadOnly="True" Background="#FFF0F0F0" VerticalScrollBarVisibility="Auto"/> </Grid> </Window> "@ -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window' #endregion Load XAML #region Load Form add-type -AssemblyName presentationframework try { $TestForm = [Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $XAMLTest) ) } catch { Write-Host "Cannot load Windows.Markup.XamlReader" } $XAMLTest.SelectNodes("//*[@Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $TestForm.FindName($_.Name)} $btn_search.Add_Click{ $SearchParams = @{ testform = $TestForm btn_search = $btn_search lst_search = $lst_search txt_message = $txt_message lmi_exportcsv = $lmi_exportcsv services = $services } Invoke-ServiceSearch @SearchParams } $lmi_exportcsv.Add_Click{ $txt_message.AppendText("`nMain: $($shareddata.services.Name)") } #endregion Load Form [void]$TestForm.ShowDialog() $shareddata.services
\_(ツ)_/
- Edited by jrv Thursday, October 17, 2019 6:52 AM
- Marked as answer by peterlustig_123 Friday, October 18, 2019 5:06 AM
Thursday, October 17, 2019 6:46 AM
All replies
-
$shareddata = [hashtable]::Synchronized(@{ testform = $testform lst_search = $lst_search btn_search = $btn_search txt_message = $txt_message services = $services error = $error } )
\_(ツ)_/
Thursday, October 17, 2019 6:10 AM -
Hello jrv,
thanks for your answer, but i dont get it what you mean.
The variable $shareddata is definded in line 37.
Regards,
Tom
Thursday, October 17, 2019 6:14 AM -
It is defined wrong. It needs to be defined as a sync hash.
\_(ツ)_/
Thursday, October 17, 2019 6:19 AM -
Hi jrv,
it is :
$script:sharedData = [HashTable]::Synchronized(@{})
Regards,
TomThursday, October 17, 2019 6:24 AM -
Sorry it is hard to read your code.
\_(ツ)_/
Thursday, October 17, 2019 6:25 AM -
Hi,
i had it before inside the function but i thought the scope of the variable is perhaps the problem so i defined it in main with scope script.
Regards,
Tom
Thursday, October 17, 2019 6:27 AM -
You create the variable outside of the form then create a new version insid of teh form.You cannot do both or the function versio will be created only inside the function and it will not be the correct type.
\_(ツ)_/
- Edited by jrv Thursday, October 17, 2019 6:33 AM
Thursday, October 17, 2019 6:32 AM -
#region functions function Invoke-ServiceSearch{ [CmdletBinding()] param ( $Testform, $lst_search, $btn_search, $txt_message, $lmi_exportcsv, $services ) $code = { $shareddata.services = Get-Service $sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.lst_search.ItemsSource = $sharedData.services},'Normal') $sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.txt_message.AppendText("Runspace: $($Shareddata.services.Name)")}) #$sharedData.testform.Dispatcher.invoke([action]{$null = $sharedData.lmi_exportcsv.IsEnabled="True"}) } $script:sharedData = [HashTable]::Synchronized(@{ testform = $testform lst_search = $lst_search btn_search = $btn_search txt_message = $txt_message services = $services error = $error } )
#generate a new runspace $runspace = [RunSpaceFactory]::CreateRunspace() $runspace.ApartmentState = 'STA' $runspace.ThreadOptions = 'ReuseThread' $runspace.Open() #add shared variable to runspace $runspace.SessionStateProxy.setVariable("sharedData", $sharedData) #generate a new psthread and add script code $ps = [PowerShell]::Create().AddScript($code) $ps.Runspace = $runspace $global:handle = $ps.BeginInvoke() } #endregion functions #region Load XAML [xml]$script:XAMLTest = @" <Window x:Class="Test.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:Test" mc:Ignorable="d" Title="Test" Height="479.303" Width="320.697" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Background="White"> <Grid Margin="0,1,2,-1" Background="#FFF0F0F0"> <Button x:Name="btn_search" Content="Search" HorizontalAlignment="Left" Margin="111,204,0,0" VerticalAlignment="Top" Width="75" FontSize="12" RenderTransformOrigin="3.108,9.031"/> <ListBox x:Name="lst_search" HorizontalAlignment="Left" Height="189" Margin="27,10,0,0" VerticalAlignment="Top" Width="269"> <ListBox.ContextMenu> <ContextMenu AllowDrop="True"> <MenuItem x:Name="lmi_exportcsv" Header="Export to CSV" IsEnabled="True"/> </ContextMenu> </ListBox.ContextMenu> </ListBox> <TextBox x:Name="txt_message" HorizontalAlignment="Left" Height="155" Margin="10,270,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="286" IsReadOnly="True" Background="#FFF0F0F0" VerticalScrollBarVisibility="Auto"/> </Grid> </Window> "@ -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window' #endregion Load XAML #region Load Form add-type -AssemblyName presentationframework try { $TestForm = [Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $XAMLTest) ) } catch { Write-Host "Cannot load Windows.Markup.XamlReader" } $XAMLTest.SelectNodes("//*[@Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $TestForm.FindName($_.Name)} $btn_search.Add_Click{ $SearchParams = @{ testform = $TestForm btn_search = $btn_search lst_search = $lst_search txt_message = $txt_message lmi_exportcsv = $lmi_exportcsv services = $services } Invoke-ServiceSearch @SearchParams } $lmi_exportcsv.Add_Click{ $txt_message.AppendText("`nMain: $($shareddata.services.Name)") } #endregion Load Form [void]$TestForm.ShowDialog() $shareddata.services
\_(ツ)_/
- Edited by jrv Thursday, October 17, 2019 6:52 AM
- Marked as answer by peterlustig_123 Friday, October 18, 2019 5:06 AM
Thursday, October 17, 2019 6:46 AM -
Hello,
sorry i dont understand it. Can you explain me a little more in detail please.
Regards,
Tom
Thursday, October 17, 2019 6:47 AM -
You define the variable where you need to use it but declare it at an outer scope so that it persists outside of the function.
Look at how I did this and how I rearranged your code to make it more understandable.
\_(ツ)_/
Thursday, October 17, 2019 6:49 AM -
Hi,
thanks a lot i will have a look.
Regards,
TomThursday, October 17, 2019 7:21 AM -
Hi jrv,
thank you very much it works for me.
Regards,
Tom
Friday, October 18, 2019 5:06 AM