92

What is the correct way of determining if a process is running, for example FireFox, and stopping it?

I did some looking around and the best way I found was this:

if((get-process "firefox" -ea SilentlyContinue) -eq $Null){ 
        echo "Not Running" 
}

else{ 
    echo "Running"
    Stop-Process -processname "firefox"
 }

Is this the ideal way of doing it? If not, what the correct way of doing so?

  • 1
    Yes it does, but I wanted to know if it is the ideal way of doing it? Is their a better way? Are their any disadvantages/advantages of doing this way? –  Feb 12 '15 at 15:53
  • 1
    I would think a kinder way to do it would be to get the main hwnd of the firefox window, and send it a WM_CLOSE and/or WM_QUIT. That may be beyond the scope of what powershell is capable of. – Lynn Crumbling Feb 12 '15 at 15:54
  • If you can, could you provide an example? –  Feb 12 '15 at 15:55
  • This is relevant: http://stackoverflow.com/a/14445452/656243 – Lynn Crumbling Feb 12 '15 at 15:56
  • If that answer completely provides a solution, let me know, and I'll mark this one as a dup. – Lynn Crumbling Feb 12 '15 at 15:57
  • The way I did it above, would it be incorrect and not ideal? –  Feb 12 '15 at 15:57
  • It's not going to give the app a chance to exit cleanly. From the docs: `Stops the specified processes without prompting for confirmation.` It's effectively killing it. Sending it a WM_CLOSE is how most application will naturally exit themselves. – Lynn Crumbling Feb 12 '15 at 16:00

5 Answers5

154

The way you're doing it you're querying for the process twice. Also Lynn raises a good point about being nice first. I'd probably try something like the following:

# get Firefox process
$firefox = Get-Process firefox -ErrorAction SilentlyContinue
if ($firefox) {
  # try gracefully first
  $firefox.CloseMainWindow()
  # kill after five seconds
  Sleep 5
  if (!$firefox.HasExited) {
    $firefox | Stop-Process -Force
  }
}
Remove-Variable firefox
Joey
  • 344,408
  • 85
  • 689
  • 683
  • 7
    Charles - this is EXACTLY what you need. Note Joey's use of the [CloseMainWindow()](https://msdn.microsoft.com/en-us/library/system.diagnostics.process.closemainwindow%28v=vs.110%29.aspx) method. Read the docs on that MSDN page. His script tries to ask the app to exit nicely... then attempts a kill. Very nice, Joey: +1. – Lynn Crumbling Feb 12 '15 at 16:02
  • I was just about to say, this is what I was looking for. –  Feb 12 '15 at 16:03
  • @CharlesWhitfield Yep, I'd mark this as the answer. And, you already have. – Lynn Crumbling Feb 12 '15 at 16:04
  • Querying for the process twice is faster than waiting a guaranteed 5 seconds every time. But I guess if you're in no rush, you can be "nice". – campbell.rw Feb 13 '15 at 01:56
  • 4
    @campbell.rw: Querying for the process only once avoids a race when the process doesn't exist anymore when you call `Stop-Process` (which does nothing on an exited process object, but throws an error on a non-existent process name). As for niceness, Firefox can take an awful long time to shut down sometimes, although it has gotten better. They should probably use a timeout that makes sense to them; this was just an example. – Joey Feb 13 '15 at 06:22
  • $firefox.CloseMainWindow() fails if there is more then one process. without it script is working for any number of processes – vadru Oct 26 '19 at 21:28
  • This is the part that was missing in my script to automatically close the game of my son, when playing more than 2 hours :). Thx – sergiu.cs Jun 08 '22 at 17:44
18

If you don't need to display exact result "running" / "not runnuning", you could simply:

ps notepad -ErrorAction SilentlyContinue | kill -PassThru

If the process was not running, you'll get no results. If it was running, you'll receive get-process output, and the process will be stopped.

AdamL
  • 12,421
  • 5
  • 50
  • 74
  • 28
    For the newbie like myself, in full `Get-Process notepad -ErrorAction SilentlyContinue | Stop-Process -PassThru` – Tom Wilson Sep 27 '16 at 15:06
5

@jmp242 - the generic System.Object type does not contain the CloseMainWindow method, but statically casting the System.Diagnostics.Process type when collecting the ProcessList variable works for me. Updated code (from this answer) with this casting (and looping changed to use ForEach-Object) is below.

function Stop-Processes {
    param(
        [parameter(Mandatory=$true)] $processName,
                                     $timeout = 5
    )
    [System.Diagnostics.Process[]]$processList = Get-Process $processName -ErrorAction SilentlyContinue

    ForEach ($Process in $processList) {
        # Try gracefully first
        $Process.CloseMainWindow() | Out-Null
    }

    # Check the 'HasExited' property for each process
    for ($i = 0 ; $i -le $timeout; $i++) {
        $AllHaveExited = $True
        $processList | ForEach-Object {
            If (-NOT $_.HasExited) {
                $AllHaveExited = $False
            }                    
        }
        If ($AllHaveExited -eq $true){
            Return
        }
        Start-Sleep 1
    }
    # If graceful close has failed, loop through 'Stop-Process'
    $processList | ForEach-Object {
        If (Get-Process -ID $_.ID -ErrorAction SilentlyContinue) {
            Stop-Process -Id $_.ID -Force -Verbose
        }
    }
}
barbsan
  • 3,418
  • 11
  • 21
  • 28
Kit
  • 61
  • 1
  • 2
3

Thanks @Joey. It's what I am looking for.

I just bring some improvements:

  • to take into account multiple processes
  • to avoid reaching the timeout when all processes have terminated
  • to package the whole in a function

function Stop-Processes {
    param(
        [parameter(Mandatory=$true)] $processName,
                                     $timeout = 5
    )
    $processList = Get-Process $processName -ErrorAction SilentlyContinue
    if ($processList) {
        # Try gracefully first
        $processList.CloseMainWindow() | Out-Null

        # Wait until all processes have terminated or until timeout
        for ($i = 0 ; $i -le $timeout; $i ++){
            $AllHaveExited = $True
            $processList | % {
                $process = $_
                If (!$process.HasExited){
                    $AllHaveExited = $False
                }                    
            }
            If ($AllHaveExited){
                Return
            }
            sleep 1
        }
        # Else: kill
        $processList | Stop-Process -Force        
    }
}
smo
  • 31
  • 1
  • One problem with this is you can end up in a state where you get ERROR: Method invocation failed because [System.Object[]] doesn't contain a method named 'CloseMainWindow'. but the $processList | Stop-Process -Force works. Not sure how to properly handle that error. – jmp242 May 11 '17 at 12:51
2

To start with process-killing, here python, my 2 cents:

Get-Process python3.9|Stop-Process
Timo
  • 2,922
  • 3
  • 29
  • 28