我一直试图确定一种方法来关闭Chrome(和其他进程),优雅地利用Python。我已经通过了一些方法,但所有这些都导致Chrome显示一条消息,指出“Chrome未正确关闭”。使用“还原”按钮。从Windows中的任务管理器关闭程序时不会发生这种情况。我编写了一个程序,利用Falcon提供API访问来关闭“托管”程序。代码与此无关,但以下是我尝试过的方法,可以关闭运行良好但不优雅的Chrome。
def stop_app(app_exe):
for process in psutil.process_iter():
if process.name() == app_exe:
process.terminate()
备用:
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)
我也试过kill()代替terminate(),各种信号类型(Windows实际上只支持三种,CTRL_BREAK_EVENT,CTRL_C_EVENT,SIGTERM)。
我也尝试过使用win32库,我遇到了同样的问题。 Win32也不是平台无关的,我发现win32 / wmi库在寻找运行的Windows进程时会占用大量的系统资源。 psutil库允许与平台无关,并且在迭代Windows进程时没有相同的性能问题。这是代码。
c = wmi.WMI()
for process in c.Win32_Process():
if process.Name == app_exe:
process.Terminate()
那里的任何人尝试类似的东西,并想出一个解决方案?
每个CristiFati建议的快速解决方案:
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)
显然,Chrome因流程终止而关闭时不满意。它有一种检测这种确切情况的机制。 它运行了一堆进程,我认为这可能是一种优雅地关闭它的方式(我可能错了,但我记得一旦我设法做到了),通过仔细选择进程被杀死的顺序(crashpad) - 处理程序,实用程序,渲染器,在主人之前),但现在我不能这样做,我也不知道实际上有多优雅。
注意:我个人认为这种行为非常有用,因为我有许多打开的Chrome窗口,每个窗口都有很多标签,每次我需要重新启动笔记本电脑时,我都会从任务管理器关闭Chrome,以便下次打开它时它会恢复当前状态。
回到我们的问题,一种方法是获取它的窗口并发送它[MS.Docs]: WM_CLOSE message(也检查[MS.Docs]: Closing the Window)。请注意,它是特定的Win!
win32api.SendMessage(0x005C0974, win32con.WM_CLOSE) # 0x005C0974 was Chrome's (main) window handle in my test
另一个适用于windows的例子:[SO]: Keyboard event not sent to window with pywin32 (@CristiFati's answer)。
一个更通用的版本,它向应用程序发送关闭消息,包括仅在系统托盘中可见的消息,包含所有必需的导入:
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")
(基于问题中的“每个CristiFati建议的快速解决方案”,但将'pid'更改为'lParam',包括所需的导入,还适用于仅在系统托盘中可见的应用程序。)