I was wanting to terminate Windows processes with a specific “command line”. The “taskkill /f /im” call to which I was familiar, only works for a specific executable, e.g. “taskkill /f /im plugin-container.exe“, written in the console (cmd.exe), will kill all running “plugin-container.exe” instances, but it is not precise enough for the termination of running processes that share the same EXE but are called with different command line arguments.

It is not easy to accomplish such selective kill. I thought of running a Windows PowerShell script to do so, after a suggestion that PowerShell’s “Get-Process” would be able to accomplish the challenge, using each process’s StartInfo.Arguments value.

But no, it doesn’t work: that property is empty for the processes I was wanting to end, which were some Firefox’s “content helpers”. For example, the line “$procs = Get-Process Firefox” in a .PS1 text script, would indeed get all the running Firefox instances, but the $proc variable made avaiable in each iteration of a “foreach($proc in $procs)” iterator would have an empty $proc.StartInfo.Arguments property.

I had never written a PowerShell script before, so in a matter of minutes I had to learn the basics:
– write a plain text file,
– save it as “some-name-you-choose.PS1“,
– and run it by providing its path, full or relative, before the script name, e.g. “./myScript.PS1” instead of “myScript.PS1”.

Regarding PowerShell scripts’ syntax, I progressed plenty, since I was starting from zero, and achieved a working file, but in the end I am NOT using it. I decided there is a cleaner solution to my issue, which was to avoid some particular content processes that modern versions of Firefox create, to handle multiple open tabs: I just disabled Firefox’s “browser.tabs.remote.autostart“, setting it to false, using “about:config“.

However, the script, as it evolved, taught me about PowerShell, and I dare share it in its original, raw, unedited ugliness; just notice that # started lines are comments for my learning):

#contains a bad, but useful example:
#https://superuser.com/questions/52159/kill-a-process-with-a-specific-command-line-from-command-line
#$procs = Get-Process Firefox #does not work, has no CommandLine property

#good tip https://www.ravichaganti.com/blog/quick-powershell-tip-get-process-commandline-information/
$procs = Get-WmiObject win32_process -Filter "Name like '%Firefox%'";

#320 MB max per contentproc tab
#not the way to declare constants
#const MAX_WORKING_SET_SIZE = 320*1024*1024; #MB*KB*BYTES
#can't do math here
#Set-Variable -Name MAX_WORKING_SET_SIZE -Value 320*1024*1024 -Option Constant;
Set-Variable -Name MAX_WORKING_SET_SIZE -Value 335544320 -Option Constant;

#better to just taskkill /f /im plugin-container.exe
Set-Variable -Name STR_SIGNATURE_OF_FIREFOX_WITH_FLASH_CONTENT -Value "C:\Windows\system32\Macromed\Flash\" -Option Constant;

foreach($proc in $procs)
{
    $path = $proc.Path; #e.g. "c:\wp\inet\ffox\firefox.exe"
    echo "Path: $path";

    $description = $proc.Description;
    echo "Description: $description"; #e.g. "Firefox"

    #$ts = $proc.StartInfo.ToString(); #outputs System.Diagnostics.ProcessStartInfo
    #echo "proc.StartInfo.toString(): $ts";

    #$args = $proc.StartInfo.Arguments; #does NOT contain the command line
    #echo "StartInfo.Arguments: $args"; #empty!

    $commandLine = $proc.CommandLine;
    echo "CommandLine: $CommandLine";

    $iWorkingSetSizeInBytes = $proc.WorkingSetSize;
    
    #https://stackoverflow.com/questions/18877580/powershell-and-the-contains-operator
    #Contains does NOT handle regexp
    
    #logical operators in PowerShell
    #https://4sysops.com/archives/powershell-logical-operators/
        #-and -or -xor -not
    
    $bMatchesCertainCommandLine = $commandLine.Contains("-contentproc --channel=");

    #bad way to compare
    #$bExceedsMaxWorkingSet = $iWorkingSetSizeInBytes > MAX_WORKING_SET_SIZE;
    #comparison operators
    #The following operators are all Case-Insensitive by default:
        # -eq             Equal
        # -ne             Not equal
        # -ge             Greater than or equal
        # -gt             Greater than
        # -lt             Less than
        # -le             Less than or equal
        # -like           Wildcard comparison
        # -notlike        Wildcard comparison
        # -match          Regular expression comparison
        # -notmatch       Regular expression comparison
        # -replace        Replace operator
        # -contains       Containment operator
        # -notcontains    Containment operator
        # -in             Like –contains, but with the operands reversed.(PowerShell 3.0)
        # -notin          Like –notcontains, but with the operands reversed.(PowerShell 3.0)

    #I don't know why I can't do what follows
    #$bExceedsMaxWorkingSet = $iWorkingSetSizeInBytes -ge MAX_WORKING_SET_SIZE;
    $bExceedsMaxWorkingSet = $iWorkingSetSizeInBytes -ge 335544320;

    $bShouldBeKilled = $bMatchesCertainCommandLine -and $bExceedsMaxWorkingSet;
    if($bShouldBeKilled)
    {
        #echo "should kill $proc.ProcessId"; #won't write like evaluative strings in PHP
        $processId = $proc.ProcessId;
        $handle = $proc.Handle; #should match ProcessId
        echo "About to kill process with ProcessId $processId";
        #kill $proc; #wrong!
        $proc.Terminate();
    }#if process should be killed
}#for every process found

Leave a Reply