1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Quits Chrome. 6 7This script sends a WM_CLOSE message to each window of Chrome and waits until 8the process terminates. 9""" 10 11import optparse 12import os 13import pywintypes 14import sys 15import time 16import win32con 17import win32gui 18import winerror 19 20import chrome_helper 21 22 23def CloseWindows(process_path): 24 """Closes all windows owned by processes whose exe path is |process_path|. 25 26 Args: 27 process_path: The path to the executable whose processes will have their 28 windows closed. 29 30 Returns: 31 A boolean indicating whether the processes successfully terminated within 32 25 seconds. 33 """ 34 start_time = time.time() 35 while time.time() - start_time < 25: 36 process_ids = chrome_helper.GetProcessIDs(process_path) 37 if not process_ids: 38 return True 39 40 for hwnd in chrome_helper.GetWindowHandles(process_ids): 41 try: 42 win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) 43 except pywintypes.error as error: 44 # It's normal that some window handles have become invalid. 45 if error.args[0] != winerror.ERROR_INVALID_WINDOW_HANDLE: 46 raise 47 time.sleep(0.1) 48 return False 49 50 51def KillNamedProcess(process_path): 52 """ Kills all running exes with the same name as the exe at |process_path|. 53 54 Args: 55 process_path: The path to an executable. 56 57 Returns: 58 True if running executables were successfully killed. False otherwise. 59 """ 60 return os.system('taskkill /f /im %s' % os.path.basename(process_path)) == 0 61 62 63def QuitChrome(chrome_path): 64 """ Tries to quit chrome in a safe way. If there is still an open instance 65 after a timeout delay, the process is killed the hard way. 66 67 Args: 68 chrome_path: The path to chrome.exe. 69 """ 70 if not CloseWindows(chrome_path): 71 # TODO(robertshield): Investigate why Chrome occasionally doesn't shut down. 72 sys.stderr.write('Warning: Chrome not responding to window closure. ' 73 'Killing all processes belonging to %s\n' % chrome_path) 74 KillNamedProcess(chrome_path) 75 76 77def main(): 78 usage = 'usage: %prog chrome_path' 79 parser = optparse.OptionParser(usage, description='Quit Chrome.') 80 _, args = parser.parse_args() 81 if len(args) != 1: 82 parser.error('Incorrect number of arguments.') 83 chrome_path = args[0] 84 85 QuitChrome(chrome_path) 86 return 0 87 88 89if __name__ == '__main__': 90 sys.exit(main()) 91