wpfpowershellxamlprocesskill-process

Kill process based upon process Id powershell - xaml listview


I have a xaml listview with a button. The listview shows all processes that have powershell* or pwsh* in them. I want to be able to kill any of the processes by clicking a button but so far nothing I've tried has worked. Here is my code so far:

[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$input = @'
<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:PowershellListView"
        Title="End Processes" Height="450" Width="870">
    <Grid>
        <Button Name="btnEndProc" Content="End Process" HorizontalAlignment="Left" Height="45" Margin="112,341,0,0" VerticalAlignment="Top" Width="110"/>
        <ListView Name="listdisk" HorizontalAlignment="Left" Height="200" Margin="12,107,-140,0" VerticalAlignment="Top" Width="840">
           <ListView.View>
                <GridView>
                    <GridViewColumn Header="Process ID" DisplayMemberBinding ="{Binding ProcessID}" Width="100"/>
                    <GridViewColumn Header="Name" DisplayMemberBinding ="{Binding Name}" Width="255"/>
                    <GridViewColumn Header="Handle Count" DisplayMemberBinding ="{Binding HandleCount}" Width="105"/>
                    <GridViewColumn Header="Working Set Size" DisplayMemberBinding ="{Binding Workingsetsize}" Width="100"/>
                    <GridViewColumn Header="Virtual Size" DisplayMemberBinding ="{Binding VirtualSize}" Width="105"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>
'@


#region code
[xml]$xaml = $input
$input = $input -replace '^window.*', '<window' -replace 'mc:ignorable="d"','' -replace "x:N",'N' -replace 'x:Class="\S+"',''

#Read XAML
$xmlreader=(New-Object System.Xml.XmlNodeReader $xaml)
$xamlForm=[Windows.Markup.XamlReader]::Load($xmlReader)
$xaml.SelectNodes("//*[@Name]") | ForEach-Object -Process {
    Set-variable -name ($_.Name) -Value $xamlForm.Findname($_.Name)
    }
#endregion code

$cim = Get-CimInstance -ClassName Win32_Process -Filter "Name like 'Powershell%' or Name like 'pwsh%'" 
$procids = Get-CimInstance -ClassName Win32_Process -Filter "Name like 'Powershell%' or Name like 'pwsh%'" | select-object -expandproperty ProcessId

#region XAML Controls

#endregion
$xamlForm.activate()
$listdisk.ItemsSource = $cim

$process = Get-Process -id $cim
write-host $process

$xamlForm.showdialog() | out-null

$btnEndProc.add_click({
    write-host "$listdisk.items.CurrentItem.ProcessId"
    $listdisk.items.currentitem.Processid.remove
    [System.Windows.Forms.Application]::DoEvents()
    })

As you can see, at the end I was just doing some testing with the write-hosts but they're coming up empty. $procids is returning process ids.

Help please. I've been working on this for days.


Solution


  • Below is a self-contained, streamlined, corrected form of your code that uses Stop-Process to kill the selected process.
    However, to be safe it uses the -WhatIf common parameter to preview the operation. Remove -WhatIf once you're sure the the correct process is being targeted.

    Add-Type -AssemblyName PresentationFramework
    
    [xml] $xaml = @'
    <Window 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:PowershellListView"
            Title="End Processes" Height="450" Width="870">
        <Grid>
            <Button Name="btnEndProc" Content="End Process" HorizontalAlignment="Left" Height="45" Margin="112,341,0,0" VerticalAlignment="Top" Width="110"/>
            <!-- 
              NOTE THE REQUIRED USE OF IsSynchronizedWithCurrentItem="True" 
              Also, to keep this demo simple, SelectionMode="Single" ensures that only ONE process can be selected at time.
            -->
            <ListView Name="listProcesses" HorizontalAlignment="Left" Height="200" Margin="12,107,-140,0" VerticalAlignment="Top" Width="840" IsSynchronizedWithCurrentItem="True" SelectionMode="Single">
               <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Process ID" DisplayMemberBinding ="{Binding ProcessID}" Width="100"/>
                        <GridViewColumn Header="Name" DisplayMemberBinding ="{Binding Name}" Width="255"/>
                        <GridViewColumn Header="Handle Count" DisplayMemberBinding ="{Binding HandleCount}" Width="105"/>
                        <GridViewColumn Header="Working Set Size" DisplayMemberBinding ="{Binding Workingsetsize}" Width="100"/>
                        <GridViewColumn Header="Virtual Size" DisplayMemberBinding ="{Binding VirtualSize}" Width="105"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid>
    </Window>
    '@ -replace '^window.*', '<window' -replace 'mc:ignorable="d"', '' -replace "x:N", 'N' -replace 'x:Class="\S+"', ''
    
    
    # Parse the XAML and create PowerShell variables named for each control.
    $xmlreader = (New-Object System.Xml.XmlNodeReader $xaml)
    $xamlForm = [Windows.Markup.XamlReader]::Load($xmlReader)
    $xaml.SelectNodes("//*[@Name]") | ForEach-Object -Process {
      Set-variable -name $_.Name -Value $xamlForm.Findname($_.Name)
    }
    
    # Get the processes of interest...
    # NOTE: Using a [System.Collections.ArrayList] instance allows later modification of the list of processes, which
    #       in turn allows removing items from the list view.
    [System.Collections.ArrayList] $processes = Get-CimInstance -ClassName Win32_Process -Filter "Name like 'Powershell%' or Name like 'pwsh%'" 
    # ... and bind them to the list control.
    $listProcesses.ItemsSource = $processes
    
    $btnEndProc.add_click({
        $selectedProcess = $listProcesses.items.CurrentItem
        $selectedProcessId = $selectedProcess.ProcessId
        Write-Verbose -Verbose "Forcefully terminating process with ID $selectedProcessId..."
        # NOTE: -WhatIf  *previews* the operation. Remove `-WhatIf` and re-execute once you're sure the operation will do what you want.
        Stop-Process -Force -ID $selectedProcessId -WhatIf
        # Remove the terminated process from the list view and remove the selection.
        $listProcesses.items.SourceCollection.Remove($selectedProcess)
        $listProcesses.items.Refresh()
        $listProcesses.SelectedIndex = -1
      })
    
    # Enable the button only if a list item is selected.
    $listProcesses.add_SelectionChanged({
        $btnEndProc.IsEnabled = $this.SelectedIndex -ne -1
    })
    
    # Make sure that the window receives focus when it is first displayed.
    $xamlForm.add_Loaded({ $this.Activate() })
    
    # Show the WPF window modally (blocking call).
    $null = $xamlForm.ShowDialog()