Search code examples
powershellprocessgoogle-drive-apiterminate

Powershell - CloseMainWindow work for all?


I'm looking for a way to gracefully close/quit the GoogleDrive app which runs under the process GoogleDriveFS.

get-process GoogleDriveFS

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    219      16    10796       5732       0.05   4392   1 GoogleDriveFS
    333      22    11820      32364       0.17   8424   1 GoogleDriveFS
    297      19    16528      34860       0.06  12036   1 GoogleDriveFS
    245      17    10472      23992       0.03  14296   1 GoogleDriveFS
    572      26    52256      82728       0.84  17788   1 GoogleDriveFS
    518      21    28668      68208       0.44  18460   1 GoogleDriveFS
   1024      59    47016      89396      27.95  19452   1 GoogleDriveFS

is something like Process.CloseMainWindow Method suitable for this ? or is there a better way to ensure the app isn't running?


Solution

  • tl;dr

    System.Diagnostics.Process.CloseMainWindow() will not work, for the reasons explained in the bottom section.

    Note:

    • If the target processes weren't started from your own user account, you'll need to run the following from an elevated (run as admin) session.

    You can try the following to achieve graceful termination, but there's no guarantee it will work:

    # Asks all GoogleDriveFS processes to terminate, which they may or may not do.
    # A status line is output to stdout for each targeted process, 
    # indicating whether the termination request was successfully *sent*.
    # Note: ".exe" must be used, whereas it mustn't be 
    #       with PowerShell's *-Process cmdlets.
    taskkill.exe /im GoogleDriveFS.exe
    

    If it doesn't, forceful termination is your only option, which is most easily accomplished with:

    # !! Forcefully terminates all GoogleDriveFS, without cleanup.
    Stop-Process -Force -Name GoogleDriveFS
    

    Note: As discussed below, Stop-Process always terminates forcefully. The only function of the -Force switch is to suppress a potential confirmation prompt that is presented when you attempt to terminate processes belonging to a different user (only works with elevation).

    Here's a snippet that first tries graceful termination, then falls back to forceful termination after a specifiable timeout:

    $processName = 'GoogleDriveFS'
    $timeOutSecs = 2
    
    # Get all existing processes of interest.
    $processes = Get-Process -ErrorAction Ignore -Name $processName
    
    if (-not $processes) {
      Write-Verbose -Verbose "No $processName processes running."
    } else {
      # Ask the processes to terminate, which they may or may not do.
      taskkill.exe /im "$processName.exe" *>$null 
      try { 
       # Wait for up to $timeOutSecs seconds for the processes to - 
       # potentially - terminate gracefully.
       $processes | Wait-Process -ErrorAction Stop -Timeout $timeOutSecs
      } catch {
        Write-Warning "Forcefully terminating (remaining) $processName processes..."
        # Note: This assumes that you don't care about any new 
        #       processes that may have launched since Get-Process was called.
        $processes | Stop-Process -Force
      }
    }
    

    • On Windows, graceful termination is fundamentally only an option for GUI-subsystem applications, i.e. processes that have a main window (whether visible or not) and therefore a message loop to which the WM_CLOSE message can be posted.

      • In other words: you cannot ask console applications on Windows to terminate gracefully (unless they implement some application-specific custom mechanism through which other processes can request termination).

      • For supported applications, there are important considerations:

        • Termination isn't guaranteed, and even if it does happen, its timing isn't guaranteed:

          • The target process may be in a state where it cannot process the WM_CLOSE message, such as when it happens to be displaying a modal dialog at the time or happens to be stuck.
          • The target process may quietly refuse to terminate.
          • The target process may put up a modal dialog to confirm the intent to terminate, notably when trying to close an editor-like application that has an unsaved document open.
        • Therefore, if you need to ensure termination, you'll have to monitor the process for actual termination afterwards, and possibly terminate it forcefully after a suitable timeout period.

    • At the Windows API level, it doesn't matter if the targeted main window is visible or not, so that even (GUI-subsystem) processes that by design run invisibly - as GoogleDriveFS.exe appears to be - can be targeted with a WM_CLOSE message.

      • While System.Diagnostics.Process.CloseMainWindow() is designed to request graceful termination of a given process by sending a WM_CLOSE message to its main window, it unfortunately doesn't find that window if it happens to be invisible (hidden) (still applies as of .NET 6.0)

      • By contrast, the taskkill.exe utility does not have this limitation.

      • A limitation that BOTH methods share is the inability to target processes that are UWP / Microsoft Store applications.

        • However, this applies only to "pure" UWP applications (e.g, Settings, Calculator), and not to desktop applications packaged as UWP apps (e.g., Windows Terminal, Microsoft Edge).
        • The reason is that both methods rely on the EnumWindows WinAPI method, which only supports desktop applications.
        • However, manually finding a UWP application's main window via FindWindowEx and posting WM_CLOSE to it, is possible.