Reputation: 468
I have been trying to determine a method to close Chrome (and other processes) gracefully utilizing Python. I have gone through a few methods but all result in Chrome displaying a message stating "Chrome didn't shut down correctly." with a Restore button. This doesn't happen when you shut down a program from task manager in Windows. I have written a program utilizing Falcon to provide API access to shut down 'managed' programs. The code for that isn't relevant, but here are the methods I have tried that do work to shut down Chrome that work well, but not gracefully.
def stop_app(app_exe):
for process in psutil.process_iter():
if process.name() == app_exe:
process.terminate()
Alternate:
for process in psutil.process_iter():
if process.name() == app_exe:
children = process.children(recursive=True)
children.append(process)
for p in children:
p.send_signal(signal.CTRL_BREAK_EVENT)
I have also tried kill() in place of terminate(), various signal types (Windows really only supports three, CTRL_BREAK_EVENT, CNTL_C_EVENT, SIGTERM).
I have also tried using the win32 library and I had the same issue. Win32 also isn't platform agnostic and I had found that win32/wmi libraries utilize a lot of system resources when looking for running windows processes. The psutil library allows for being platform agnostic AND doesn't have the same performance issue when iterating through the Windows processes. Here is the code for that.
c = wmi.WMI()
for process in c.Win32_Process():
if process.Name == app_exe:
process.Terminate()
Anyone out there try something similar and come up with a solution?
Quick solution per CristiFati recommendations.:
def enumWindowsProc(hwnd, lParam):
if (lParam is None) or ((lParam is not None) and
win32process.GetWindowThreadProcessId(hwnd)[1] == pid)):
text = win32gui.GetWindowText(hwnd)
if text:
wStyle = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE)
if wStyle & win32con.WS_VISIBLE:
win32api.SendMessage(hwnd, win32con.WM_CLOSE)
def stop_app(app_exe):
for process in psutil.process_iter():
if process.name() == app_exe:
win32gui.EnumWindows(enumWindowsProc, process.pid)
Upvotes: 2
Views: 2906
Reputation: 1280
A more generic version that sends the close message to apps, including those only visible in the system tray, with all included needed imports:
import win32con
import win32api
import win32gui
import win32process
import psutil
def enumWindowsProc(hwnd, lParam):
if (lParam is None) or ((lParam is not None) and win32process.GetWindowThreadProcessId(hwnd)[1] == lParam):
text = win32gui.GetWindowText(hwnd)
if text:
win32api.SendMessage(hwnd, win32con.WM_CLOSE)
def stop_app(app_exe):
for process in psutil.process_iter():
if process.name() == app_exe:
win32gui.EnumWindows(enumWindowsProc, process.pid)
stop_app("appname.exe")
(Based off of 'Quick solution per CristiFati recommendation' in the Question, but changes 'pid' to 'lParam', includes needed imports, and also works on apps only visible in the system tray.)
Upvotes: 2
Reputation: 41116
Apparently, Chrome is not happy when being closed by process termination. It has a mechanism of detecting this exact scenario.
It has a bunch of processes running, and I think it could be a way of gracefully closing it (I might be wrong, but I remember that once I managed to do it), by carefully selecting the order which the processes are killed (crashpad-handler, utility, renderers, before the master one), but now I can't do it, neither do I know how gracefully that actually is.
Note: I personally consider this behavior very useful, because I have many opened Chrome windows each with many tabs, and every time I need to restart my laptop, I close Chrome from Task Manager, so that next time I open it it will restore the current state.
Back, to our problem, one way would be to get its window and sending it the [MS.Docs]: WM_CLOSE message (also check [MS.Docs]: Closing the Window). Note that it's Win specific!
Code that closes the window:
win32api.SendMessage(0x005C0974, win32con.WM_CLOSE) # 0x005C0974 was Chrome's (main) window handle in my test
Another example that works with windows: [SO]: Keyboard event not sent to window with pywin32 (@CristiFati's answer).
Upvotes: 2