python win32com 关闭 Excel 进程

5 投票
1 回答
19854 浏览
提问于 2025-04-17 21:15

我在用Python修改Excel表格的时候,完全搞不懂怎么处理恢复过程。

import win32com.client
class XlsClass:
    def __init__(self ,filename=None ,*,Visible=False ,Alerts=False):
        self.xlApp = win32com.client.Dispatch('Excel.Application')
        self.xlApp.Visible = Visible
        self.xlApp.DisplayAlerts = Alerts
        if filename:
            self.filename = filename
            self.xlBook = self.xlApp.Workbooks.Open(filename)
        else:
            self.xlBook = self.xlApp.Workbooks.Add()
            self.filename = ''

    def __del__(self):
        self.xlBook.Close()
        self.xlApp.Quit()

有时候代码运行得很好,但有时候Python会报错,比如说'自我.xlApp.Visible无法设置?'。这个错误总是在一个循环中出现,就像:

for fname in filelist:
    xlbook = XlsClass(fname)
    #do something

然后我查看了我的'任务管理器',注意到

xlbook = Dispatch('Excel.Application') 

会创建一个名为'EXCEL.EXE*32'的进程。当我输入'xlbook.Quit()'时,这个进程还是在那里!?所以可能是因为这个残留的进程导致了'无法设置'的错误?在我调用'Dispatch'函数后,怎么才能完全关闭它呢?

del xlbook

无法结束这个进程,那它是怎么工作的呢?

英语不好,期待帮助……谢谢。

================================================

2014/3/10:我又遇到这个错误,并捕获了错误追踪信息……

Traceback (most recent call last):
  File "C:\work_daily\work_RecPy\__RecLib\XlsClass.py", line 9, in __init__
    self.xlApp.Visible = Visible
  File "C:\Program Files\python33\lib\site-packages\win32com\client\dynamic.py", 
  line 576, in __setattr__
  raise AttributeError("Property '%s.%s' can not be set." % (self._username_,attr))
AttributeError: Property 'Excel.Application.Visible' can not be set.

我尝试了del self.xlApp或者xlbook = None,在创建一个新的XlsClass()之前,但似乎没有效果……

1 个回答

1

这可能是因为Python的垃圾回收机制造成的:每次循环时,你都会创建一个XlsClass的实例;当一次循环结束后,这个XlsClass的实例就没有被引用了,所以它可以被垃圾回收,但这个过程可能不会立即发生。因此,下次循环时,你又创建了一个新的Excel实例,而之前的那个可能还没有完全关闭。你可以尝试在下一次循环之前强制进行垃圾回收:

for fname in filelist:
    xlbook = XlsClass(fname)
    #do something
    xlbook = None
    gc.collect()

更新:

如果这样做不管用,你可以尝试连接到一个正在运行的实例,也就是说,一旦启动了应用程序,就不要关闭它,只关闭工作簿。在这种情况下,你可能想要这样做:

class XlsClass:
    def __init__(self, xlApp, filename=None ,*,Visible=False ,Alerts=False):
        self.xlApp = xlApp
        self.xlApp.Visible = Visible
        ...

    def otherMethod(self):
        # ....

    def close(self):
        self.xlBook.Close()
        self.xlBook = None
        self.xlApp = None
        # don't do anything with self.xlApp or self

xlApp = win32com.client.Dispatch('Excel.Application')
for fname in filelist:
    xlbook = XlsClass(xlApp, fname)
    # do something with xlbook
    xlbook.close()

注意,显式地关闭工作簿比等待垃圾回收器执行__del__要好。

另外,你可以使用win32com.client.GetObject (filename),如果已经有实例存在,它会获取这个实例;如果没有,它会自动创建一个;你可以把GetObject放在__init__里。在这种情况下,你需要在退出脚本之前关闭Excel(如果它是打开的),通过最后一次调用GetActiveObject(见Python/win32com - 检查程序是否打开),然后再调用Close。在我的win32com脚本中,我会在脚本开始时检查Excel是否已经在运行,如果是的话,我会打印一个错误提示,要求用户关闭,这样可以让用户清理并退出,以便从一个已知的状态开始。

撰写回答