使用Python的win32com无法完全关闭Excel

30

这是我的代码,我在VBA、.NET框架中找到了许多答案,但情况非常奇怪。当我执行它时,Excel会关闭。

from win32com.client import DispatchEx
excel = DispatchEx('Excel.Application')
wbs = excel.Workbooks
wbs.Close()
excel.Quit()
wbs = None
excel = None # <-- Excel Closes here

但是当我执行以下操作时,它没有关闭。
excel = DispatchEx('Excel.Application')
wbs = excel.Workbooks
wb = wbs.Open('D:\\Xaguar\\A1.xlsm')
wb.Close(False)
wbs.Close()
excel.Quit()
wb = None
wbs = None
excel = None  # <-- NOT Closing !!!

我在Stack Overflow的问题“Excel process remains open after interop; traditional method not working”中发现了一些可能的答案。问题是这不是Python,并且我找不到Marshal.ReleaseComObjectGC。我查看了所有在...site-packages/win32com和其他地方的演示。

即使我只能获取PID并将其终止,也不会困扰我。

我在Kill process based on window name (win32)中找到了一个解决方法。

可能不是正确的方法,但解决方法是:

def close_excel_by_force(excel):
    import win32process
    import win32gui
    import win32api
    import win32con

    # Get the window's process id's
    hwnd = excel.Hwnd
    t, p = win32process.GetWindowThreadProcessId(hwnd)
    # Ask window nicely to close
    win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
    # Allow some time for app to close
    time.sleep(10)
    # If the application didn't close, force close
    try:
        handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, p)
        if handle:
            win32api.TerminateProcess(handle, 0)
            win32api.CloseHandle(handle)
    except:
        pass

excel = DispatchEx('Excel.Application')
wbs = excel.Workbooks
wb = wbs.Open('D:\\Xaguar\\A1.xlsm')
wb.Close(False)
wbs.Close()
excel.Quit()
wb = None
wbs = None
close_excel_by_force(excel) # <--- YOU #@#$# DIEEEEE!! DIEEEE!!!

尝试在调用 wbs.Open(...) 之前添加行 excel.DisplayAlerts = False,看看是否有帮助。 - srgerg
不工作,对于Excel和COM来说是一个真正的痛苦。 - sacabuche
我找到了方法,但不使用COM。 - sacabuche
更快、更干净、更简单的答案在这里:https://dev59.com/N3bZa4cB1Zd3GeqPBhFE#18421814 - Tom
强制关闭Excel进程不是一个好主意,因为用户可能还有其他未想要关闭的Excel文件打开。 - Chris Collett
8个回答

13

试试这个:

wbs.Close()
excel.Quit()
del excel # this line removed it from task manager in my case

1
删除 Excel 不会从任务管理器中移除 Excel 进程。 - Heetola
尝试将 wbs.Close() 更改为 wbs.Close(False),以在不保存的情况下关闭工作簿。 - Jonathan Kunze
这将关闭所有打开的工作簿,即使是你的应用程序没有打开的。请尝试使用此答案 - Chris Collett

4

我发现通过win32com杀死Excel进程并不总是可靠的。这些包括excel.Application.Quit()和像上面建议的del excel

我发现确保Excel进程已结束的唯一方法是物理上杀死EXCEL.EXE进程:

import psutil

def kill_excel():
    for proc in psutil.process_iter():
        if proc.name() == "EXCEL.EXE":
            proc.kill()

缺点是这个脚本显然会关闭当前正在运行的所有Excel进程。 如果你能接受这一点,那么这不是一个坏选择。


4

我所做的是确保像这样取消引用您沿途分配的任何变量:

import win32com.client as win32

fileDirectory = 'Hello World.xlsx'

#excelFile = win32.Dispatch('Excel.Application')
excelFile = win32.gencache.EnsureDispatch('Excel.Application')

excelFile.Visible = True
excelFile.DisplayAlerts = True

wb = excelFile.Workbooks.Open(fileDirectory)
ws = wb.Sheets("Sell")

ws.Range("D1").Value = "Hello World!"
ws = None

wb.Close(False)
wb = None

excelFile.Quit()
excelFile = None

它可以与任意一种调度(Dispatch)格式配合使用。


3

我有一些使用Excel的文件:

这句话需要翻译吗?
self.excel.Application.Quit()

2
你是在使用DispatchEx还是Dispatch?它是真的关闭了还是一直在运行?因为实际上当我关闭它时,它似乎已经关闭了,但是在任务管理器中它仍然保持活动状态。 - sacabuche
抱歉,我使用了EnsureDispatch,这可能会有所不同。之后它完全关闭了。 - Aaron S

2

对于来自搜索引擎的新手:

在我的试验中,这个答案说得很对 Python应该处理COM对象的生命周期...。 只需设置excel = None,Excel.exe PID现在或过一段时间后将自动关闭。如果您仍然发现Excel.exe存在很长时间,请调用python gc collect。

import gc
gc.collect()

到目前为止,唯一有效的是这个,谢谢! - nmz787

2

Python应该处理COM对象的生命周期。只需设置excel = None。有关参考,请参见此处:

# CH9 says: Python manages COM lifetimes automatically for you; when your excel 
#           variable is no longer used, Excel automatically closes. In Python,
#           the simplest way to remove this variable is to assign it to another 
#           value. If you use the following code, notice that Excel vanishes 
#           from the screen; it knows there are no longer any programs referring to it. 

src:

http://www.icodeguru.com/WebServer/Python-Programming-on-Win32/ch05.htm#:~:text=xl%20=%20None


1
大多数其他答案都采用了完全关闭Excel的方法。问题是,这将关闭可能已经打开的任何其他Excel实例。
为了使您打开的工作簿真正关闭,您需要关闭并删除您的工作簿(从excel.Workbooks.Open返回的值)。
import win32com.client

excel = win32com.client.Dispatch('Excel.Application')
workbook = excel.Workbooks.Open(r'your\excel\file.xlsm')
# Do whatever you need to with the workbook
workbook.Close(False)  # or workbook.Close(True) if you want to save it
del(workbook)

这可以用try ... except ...块来包装,以确保即使出现错误也会关闭它。例如...
import win32com.client
import logging

excel = win32com.client.Dispatch('Excel.Application')
workbook = excel.Workbooks.Open(r'your\excel\file.xlsm')
try:
    some_function_to_use_workbook(workbook)
    workbook.close(True)
except Exception as error:
    logging.error(error)
    workbook.close(False)
finally:
    del(workbook)

如果出于某种原因你确实需要关闭Excel并关闭所有打开的工作簿,那么你需要强制关闭所有工作簿,退出应用程序并删除Excel对象。
import win32com.client

excel = win32com.client.Dispatch('Excel.Application')
workbook = excel.Workbooks.Open(r'your\excel\file.xlsm')
# Do whatever you need to with the workbook
workbook.Close(False)
del(workbook)
excel.Workbooks.Close()
excel.Application.Quit()
del(excel)

0

在多次尝试失败后,这是最终对我有效的方法:

import pythoncom, win32com.client, psutil

# For testing purposes, choose whether the Excel process will stay open or not.
method = 'good'

# Start and stop Excel instance.
if method == 'bad':
    excel = win32com.client.gencache.EnsureDispatch('Excel.Application')
    excel.Application.Quit()
elif method == 'good':
    pythoncom.CoInitialize()
    excel = win32com.client.dynamic.Dispatch('Excel.Application')
    excel.Application.Quit()
    excel = None
    pythoncom.CoUninitialize()

# Check if Excel is still open, and if so, give the option to close it.
for proc in psutil.process_iter():
    if any(procstr in proc.name() for procstr in ['EXCEL']):
        print(
            'Excel process found. Close?\n[Y/N]: ',
            end=''
        )

        answer = input()

        if answer.lower() == 'y' or answer.lower() == 'yes':
            for proc in psutil.process_iter():
                if any(procstr in proc.name() for procstr in ['EXCEL']):
                    proc.kill()
        else:
            quit()

我还没有用Workbooks测试过这个脚本,但我的理解是,在调用excel.Application.Quit()之前,你还需要关闭并将它们设置为None


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接