Hi,
I'm working on a personnal project and try to learn about GUI and runspaces for multithreading purpose.
I would like to run commands ( ping , os info, network info ...) for all computers in the listview " selected computers"
open a runspacepool for each computer
the output (wich is different for each command) should be outputted in the listview "Output"
My issue if for the formating and the output data
any help would be very appreciated
here is a part of my code
##########################################################################################
# Load Assembly and Library
##########################################################################################
try{
Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms
} catch {
Throw "Failed to load Windows Presentation Framework assemblies."
}
##########################################################################################
# END Load Assembly and Library
##########################################################################################
##########################################################################################
# region Import Modules
##########################################################################################
try{
Import-Module ActiveDirectory
} catch {
Throw "Failed to import module active directoy"
}
try{
Import-Module DNSSERVER
} catch {
Throw "Failed to import module DNS SERVER"
}
##########################################################################################
# END region Import Modules
##########################################################################################
##########################################################################################
# region functions
##########################################################################################
##########################################################################################
# END region functions
##########################################################################################
#region Synchronized Collections
$runspace = [hashtable]::Synchronized(@{})
$ui = [hashtable]::Synchronized(@{})
$jobCleanup = [hashtable]::Synchronized(@{})
$jobs = [collections.arraylist]::Synchronized([collections.arraylist]@())
$collection = [hashtable]::Synchronized(@{})
#endregion Synchronized Collections
#region Extra things for Sync'ed Collections
$collection.ObservableCollection = New-Object System.Collections.ObjectModel.ObservableCollection[object]
$jobCleanup.Flag = $True
#endregion Extra things for Sync'ed Collections
#region UI Runspace Creation
$runspace.Runspace =[runspacefactory]::CreateRunspace()
$runspace.Runspace.ApartmentState = "STA"
$runspace.Runspace.Open()
$runspace.Runspace.SessionStateProxy.SetVariable("runspace",$runspace)
$runspace.Runspace.SessionStateProxy.SetVariable("ui",$ui)
$runspace.Runspace.SessionStateProxy.SetVariable("jobCleanup",$jobCleanup)
$runspace.Runspace.SessionStateProxy.SetVariable("jobs",$jobs)
$runspace.Runspace.SessionStateProxy.SetVariable("collection",$collection)
$runspace.PowerShell = [powershell]::Create().AddScript({
$ui.Error = $Error
#region Build the GUI
[xml]$xaml = @"
<Window
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title="Admin Tools" Height="933" Width="1000">
<Grid Margin="-2,0,2,-1">
<Menu HorizontalAlignment="Left" Height="18" Margin="0,-2,0,0" VerticalAlignment="Top" Width="992" Grid.RowSpan="2" Grid.ColumnSpan="4">
<MenuItem Header="_File" Name="MainFile">
<MenuItem Header="_Edit ini file" Name="miini"/>
<Separator/>
<MenuItem Header="_Import a csv to queue" Name="miimp"/>
<MenuItem Header="_Export queue to csv" Name="miexp"/>
<Separator/>
<MenuItem Header="Exit" Name="miex"/>
</MenuItem>
<MenuItem Header="_Actions" Name="MainActions">
<MenuItem Header="_Use Others Credentials" Name="miuoc"/>
<MenuItem Header="_Connect Other Domain" Name="micod"/>
</MenuItem>
<MenuItem Header="_?" Name="MainQuestionMark">
<MenuItem Header="_?" Name="miqm"/>
<MenuItem Header="_About" Name="miab"/>
</MenuItem>
</Menu>
<StatusBar x:Name="SB1" HorizontalAlignment="Left" Height="27" Margin="0,876,0,0" VerticalAlignment="Top" Width="992" RenderTransformOrigin="-0.917,-0.06">
<StatusBar.ContextMenu>
<ContextMenu/>
</StatusBar.ContextMenu>
<StatusBarItem x:Name="LABEL_SB1" Content="Ready" Height="27" VerticalAlignment="Top"/>
</StatusBar>
<Label x:Name="label_user_connected_to" Content="User :" HorizontalAlignment="Left" Margin="0,21,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.311,-0.372"/>
<TextBox x:Name="TB_user_connected_to" HorizontalAlignment="Left" Height="23" Margin="45,25,0,0" TextWrapping="Wrap" Text="current_user" VerticalAlignment="Top" Width="97" RenderTransformOrigin="0.119,-0.607"/>
<Label x:Name="label_domain_connected_to" Content="connected to domain :" HorizontalAlignment="Left" Margin="156,22,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.311,-0.372"/>
<TextBox x:Name="TB_domain_connected_to" HorizontalAlignment="Left" Height="23" Margin="290,26,0,0" TextWrapping="Wrap" Text="current_domain" VerticalAlignment="Top" Width="221"
RenderTransformOrigin="0.119,-0.607"/>
<TabControl x:Name="tabControl" HorizontalAlignment="Left" Height="823" Margin="0,53,0,0" VerticalAlignment="Top" Width="992" RenderTransformOrigin="-2.573,-0.982">
<TabItem x:Name="Tab_Computers" Header="Computers" Width="80" FontSize="11">
<Grid Background="#FFE5E5E5">
<Label x:Name="label_search_computers" Content="Search Computer(s) :" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="-0.291,-0.765" Width="123" Margin="0,5,0,0"/>
<TextBox x:Name="TB_Search_Computer" HorizontalAlignment="Left" Height="26" Margin="123,7,0,0" TextWrapping="Wrap" Text="Type hostname or IP address" VerticalAlignment="Top" Width="170"
ToolTip=" Type hostname or IP address wildcard accepted"/>
<Button x:Name="BTN_Get_All_AD_Computers" Content="Get All AD Computers" HorizontalAlignment="Left" Margin="354,7,0,0" VerticalAlignment="Top" Width="114" RenderTransformOrigin="1.199,0.404"
Height="26" ToolTip="Get all computers from active directory"/>
<Label x:Name="label_Founded_Computers" Content="Founded Computers :" HorizontalAlignment="Left" Margin="0,36,0,0" VerticalAlignment="Top" Width="127"/>
<ProgressBar x:Name="PB_Computers" HorizontalAlignment="Left" Height="18" Margin="0,781,0,0" VerticalAlignment="Top" Width="986" RenderTransformOrigin="-1.588,4.833"/>
<Label x:Name="label_Selected_Computers" Content="Selected Computers :" HorizontalAlignment="Left" Margin="0,287,0,0" VerticalAlignment="Top" Width="127"/>
<Label x:Name="label_Output_Computers" Content="Output :" HorizontalAlignment="Left" Margin="0,528,0,0" VerticalAlignment="Top" Width="127"/>
<Button x:Name="BTN_Add_Selected_Founded_Computers" Content="+" HorizontalAlignment="Left" Margin="298,163,0,0" VerticalAlignment="Top" Width="25" ToolTip="Add selected computers"
RenderTransformOrigin="-0.225,0.602"/>
<Button x:Name="BTN_Add_All_Founded_Computers" Content="++" HorizontalAlignment="Left" Margin="298,197,0,0" VerticalAlignment="Top" Width="25" ToolTip="Add all computers" RenderTransformOrigin="0.532,-0.288"/>
<Button x:Name="BTN_Remove_Selected_Selected_Computers" Content="-" HorizontalAlignment="Left" Margin="298,312,0,0" VerticalAlignment="Top" Width="25" ToolTip="Remove selected computers"
RenderTransformOrigin="0.32,-0.211"/>
<Button x:Name="BTN_Remove_All_Selected_Computers" Content="X" HorizontalAlignment="Left" Margin="298,340,0,0" VerticalAlignment="Top" Width="25" ToolTip="Remove all computers" RenderTransformOrigin="0.605,5.021"/>
<Button x:Name="BTN_Import_Computers_From_File" Content="File" HorizontalAlignment="Left" Margin="298,62,0,0" VerticalAlignment="Top" Width="25" ToolTip="Add computers From File"/>
<Button x:Name="BTN_Export_Computers_TO_File" Content="File" HorizontalAlignment="Left" Margin="298,480,0,0" VerticalAlignment="Top" Width="25" ToolTip="Export computers To File"/>
<Button x:Name="BTN_Ping" Content="Ping" HorizontalAlignment="Left" Margin="354,67,0,0" VerticalAlignment="Top" ToolTip="Test Connectivity" Width="105" Height="25"/>
<Button x:Name="BTN_OS_Info" Content="OS Infos" HorizontalAlignment="Left" Margin="354,109,0,0" VerticalAlignment="Top" ToolTip="Get infos related to OS" Width="105" Height="25"/>
<Button x:Name="BTN_Disk_Info" Content="DISK Infos" HorizontalAlignment="Left" Margin="354,146,0,0" VerticalAlignment="Top" ToolTip="Get infos related to disks" Width="105" Height="25"/>
<Button x:Name="BTN_Export_Output_Computers_TO_File" Content="Export To File :" HorizontalAlignment="Left" Margin="809,531,0,0" VerticalAlignment="Top" Width="81" ToolTip="Export Output To
File"/>
<Button x:Name="BTN_Export_Output_Computers_TO_Mail" Content="Send by Mail" HorizontalAlignment="Left" Margin="897,531,0,0" VerticalAlignment="Top" Width="79" ToolTip="Send Output by mail"/>
<Button x:Name="BTN_Search_AD_Computers" Content="Search" HorizontalAlignment="Left" Margin="298,7,0,0" VerticalAlignment="Top" Width="51" RenderTransformOrigin="1.199,0.404" Height="26"
ToolTip="search ad Computer"/>
<Label x:Name="label_Days_Computers" Content="Days :" HorizontalAlignment="Left" Margin="695,7,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.533,-0.347"/>
<TextBox x:Name="TB_Days_Computer_To_Delete" HorizontalAlignment="Left" Height="26" Margin="740,7,0,0" TextWrapping="Wrap" Text="days" VerticalAlignment="Top" Width="38"/>
<Button x:Name="BTN_Search_Days_Computers_To_Delete" Content="Search" HorizontalAlignment="Left" Margin="783,7,0,0" VerticalAlignment="Top" Width="51" RenderTransformOrigin="1.199,0.404"
Height="26" ToolTip="search Computers not connected since numbers of days"/>
<Button x:Name="BTN_Cleanup_Days_Computers_To_Delete" Content="Cleanup" HorizontalAlignment="Left" Margin="839,7,0,0" VerticalAlignment="Top" Width="51" RenderTransformOrigin="1.199,0.404"
Height="26" ToolTip="Cleanup Computers not connected since numbers of days"/>
<Button x:Name="BTN_ADD_AD_Computers" Content="ADD" HorizontalAlignment="Left" Margin="298,31,0,0" VerticalAlignment="Top" Width="51" RenderTransformOrigin="1.199,0.404" Height="26"
ToolTip="search ad Computer"/>
<ListBox x:Name="LB_Founded_Computers" HorizontalAlignment="Left" Height="155" Margin="0,61,0,0" VerticalAlignment="Top" Width="298" AllowDrop="True" SelectionMode="Multiple"/>
<ListView x:Name="LV_Output_Computer" HorizontalAlignment="Left" Height="228" Margin="0,553,0,0" VerticalAlignment="Top" Width="986">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<Button x:Name="BTN_Remove_Selected_founded_Computer" Content="-" HorizontalAlignment="Left" Margin="298,97,0,0" VerticalAlignment="Top" Width="25" ToolTip="Remove selected computers"
RenderTransformOrigin="0.32,-0.211"/>
<Button x:Name="BTN_Remove_All_Founded_Computers" Content="X" HorizontalAlignment="Left" Margin="298,121,0,0" VerticalAlignment="Top" Width="25" ToolTip="Remove all computers" RenderTransformOrigin="0.605,5.021"/>
<StatusBar x:Name="SB_Founded_Computers" HorizontalAlignment="Left" Height="24" Margin="0,217,0,0" VerticalAlignment="Top" Width="298">
<StatusBarItem x:Name="LABEL_LB_Founded_Computers" Content="" Height="24" VerticalAlignment="Top"/>
</StatusBar>
<StatusBar x:Name="SB_Selected_Computers" HorizontalAlignment="Left" Height="24" Margin="0,499,0,0" VerticalAlignment="Top" Width="298">
<StatusBarItem x:Name="LABEL_SB_LV_Selected_Computers" Content="" Height="24" VerticalAlignment="Top"/>
</StatusBar>
<ListView x:Name="LV_Selected_Computers" HorizontalAlignment="Left" Height="189" Margin="0,310,0,0" VerticalAlignment="Top" Width="298" AllowDrop="True" IsManipulationEnabled="True">
<ListView.View>
<GridView x:Name="gv1" >
<GridViewColumn x:Name = 'name' Width = '150' DisplayMemberBinding = '{Binding Path = name}' Header='Hostname'/>
<GridViewColumn x:Name = 'IP_Address' Width = '148' DisplayMemberBinding = '{Binding Path = IP_Address}' Header='Ip_Address'/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
"@
#endregion Build the GUI
#region Background runspace to clean up jobs
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("jobCleanup",$jobCleanup)
$newRunspace.SessionStateProxy.SetVariable("jobs",$jobs)
$newRunspace.SessionStateProxy.SetVariable("ui",$ui)
$jobCleanup.PowerShell = [PowerShell]::Create().AddScript({
#Routine to handle completed runspaces
Do {
[System.Threading.Monitor]::Enter($Jobs.syncroot)
Foreach($runspace in $jobs) {
If ($runspace.Handle.isCompleted) {
$runspace.powershell.EndInvoke($runspace.Handle) | Out-Null
$runspace.powershell.dispose()
$runspace.Handle = $null
$runspace.powershell = $null
}
}
#Clean out unused runspace jobs
$temphash = $jobs.clone()
$temphash | Where {
$_.runspace -eq $Null
} | ForEach {
$jobs.remove($_)
}
[System.Threading.Monitor]::Exit($Jobs.syncroot)
Start-Sleep -Seconds 1
} while ($jobCleanup.Flag)
})
$jobCleanup.PowerShell.Runspace = $newRunspace
$jobCleanup.Thread = $jobCleanup.PowerShell.BeginInvoke()
#endregion
#region Create Variables
#-------------------------------------------------------------------------------------------------------------------------------
#$scriptDir = "c:\temp"
#-------------------------------------------------------------------------------------------------------------------------------
$date = get-date -f yyyy-MM-dd-hh_mm_ss
#-------------------------------------------------------------------------------------------------------------------------------
#endregion Create Variables
#region Connect to Controls
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$ui.Window=[Windows.Markup.XamlReader]::Load( $reader )
$ui.TB_user_connected_to = $ui.Window.FindName('TB_user_connected_to')
$ui.TB_domain_connected_to = $ui.Window.FindName('TB_domain_connected_to')
$ui.SB1 = $ui.Window.FindName('SB1')
$ui.LABEL_SB1 = $ui.Window.FindName('LABEL_SB1')
$ui.TB_Search_Computer = $ui.Window.FindName('TB_Search_Computer')
$ui.LB_Founded_Computers = $ui.Window.FindName('LB_Founded_Computers')
$ui.SB_Founded_Computers = $ui.Window.FindName('SB_Founded_Computers')
$ui.LABEL_LB_Founded_Computers = $ui.Window.FindName('LABEL_LB_Founded_Computers')
$ui.LV_Selected_Computers = $ui.Window.FindName('LV_Selected_Computers')
$ui.SB_Selected_Computers = $ui.Window.FindName('SB_Selected_Computers')
$ui.LABEL_SB_LV_Selected_Computers = $ui.Window.FindName('LABEL_SB_LV_Selected_Computers')
$ui.BTN_ADD_AD_Computers = $ui.Window.FindName('BTN_ADD_AD_Computers')
$ui.BTN_Get_All_AD_Computers = $ui.Window.FindName('BTN_Get_All_AD_Computers')
$ui.BTN_Search_AD_Computers = $ui.Window.FindName('BTN_Search_AD_Computers')
$ui.BTN_Add_Selected_Founded_Computers = $ui.Window.FindName('BTN_Add_Selected_Founded_Computers')
$ui.BTN_Add_All_Founded_Computers = $ui.Window.FindName('BTN_Add_All_Founded_Computers')
$ui.LV_Output_Computer = $ui.Window.FindName('LV_Output_Computer')
$ui.PB_Computers = $ui.Window.FindName('PB_Computers')
$ui.BTN_Ping = $ui.Window.FindName('BTN_Ping')
#region UI Events
$ui.Window.Add_Loaded({
$ui.LV_Output_Computer.ItemsSource = $collection.ObservableCollection
})
$ui.TB_user_connected_to.Text = $env:USERNAME
$ui.TB_domain_connected_to.Text = $env:USERDNSDOMAIN
$ui.LABEL_SB1.content = "Ready"
$ui.LABEL_LB_Founded_Computers.content = ("List contains $($ui.LB_Founded_Computers.Items.Count) items")
$ui.LABEL_SB_LV_Selected_Computers.content = ("List contains $($ui.LABEL_SB_LV_Selected_Computers.Items.Count) items")
$ui.BTN_Add_Selected_Founded_Computers.Add_Click({
$ui.LB_Founded_Computers.selecteditems | foreach { $ui.LV_Selected_Computers.AddChild([pscustomobject]@{Name=$_}) }
$ui.LABEL_SB_LV_Selected_Computers.content = ("List contains $($ui.LV_Selected_Computers.Items.Count) items")
})
$ui.BTN_Add_All_Founded_Computers.Add_Click({
$ui.LB_Founded_Computers.items | foreach { $ui.LV_Selected_Computers.AddChild([pscustomobject]@{Name=$_}) }
$ui.LABEL_SB_LV_Selected_Computers.content = ("List contains $($ui.LV_Selected_Computers.Items.Count) items")
})
$ui.BTN_ADD_AD_Computers.Add_Click({
$ui.LB_Founded_Computers.Items.add($ui.TB_Search_Computer.text)
$ui.LABEL_LB_Founded_Computers.content = ("List contains $($ui.LB_Founded_Computers.Items.Count) items")
$ui.TB_Search_Computer.clear()
})
$ui.BTN_Ping.Add_Click({
$ui.PB_Computers.Value = 0
$ps = [powershell]::Create().AddScript({
Param ($Collection, $ui, $jobs)
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,5)
$ScriptBlock = {
Param ($ui, $Collection)
$T = Test-Connection $_.name -count 1
$Name = ($T).Address
$IP = ($T).IPV4Address.ToString()
$Object += [pscustomobject]@{
TimeStart = (Get-Date).ToString()
Name = $Name
IP = $IP
Status = "status"
}
Start-Sleep -Seconds 2
$ui.LV_Output_Computer.Dispatcher.Invoke('Normal',[action]{
[void]$collection.observableCollection.Add($Object)
#[void]$ui.LV_Output_Computer.itemssource.add($Object)
$ui.PB_Computers.Value++
})
}
$servers = $ui.LV_Selected_Computers.Items
$servers | ForEach {
$ps = [powershell]::Create()
$ps.RunspacePool = $RunspacePool
$RunspacePool.Open()
[void]$ps.AddScript($ScriptBlock).AddArgument($ui).AddArgument($Collection)
[void]$jobs.Add([pscustomobject]@{
PowerShell = $Ps
Handle = $PS.BeginInvoke()
})
}
}).AddArgument($Collection).AddArgument($ui).AddArgument($jobs)
[System.Threading.Monitor]::Enter($Jobs.syncroot)
$jobs.Add([pscustomobject]@{
PowerShell = $ps
Handle = $Ps.BeginInvoke()
})
[System.Threading.Monitor]::Exit($Jobs.syncroot)
})
##############################################################
$ui.Window.Add_Closed({
#Halt job processing
$jobCleanup.Flag = $False
#Stop all runspaces
$jobCleanup.PowerShell.Dispose()
$runspace.PowerShell.Runspace.Close()
$runspace.PowerShell.Dispose()
[gc]::Collect()
[gc]::WaitForPendingFinalizers()
})
#endregion UI Events
[void]$ui.Window.ShowDialog()
})
$runspace.PowerShell.Runspace = $runspace.Runspace
$runspace.Handle = $runspace.PowerShell.BeginInvoke()
#endregion UI Runspace Creation