30

This is my code, and I found many answers for VBA, .NET framework and is pretty strange. When I execute this, Excel closes.

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

But when I do the following, it does not close.

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 !!!

I found some possible answer in Stack Overflow question Excel process remains open after interop; traditional method not working. The problem is that is not Python, and I don't find Marshal.ReleaseComObject and GC. I looked over all the demos on ...site-packages/win32com and others.

Even it does not bother me if I can just get the PID and kill it.

I found a workaround in Kill process based on window name (win32).

May be not the proper way, but a workround is:

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!!!
Community
  • 1
  • 1
sacabuche
  • 2,781
  • 1
  • 27
  • 35

8 Answers8

13

Try this:

wbs.Close()
excel.Quit()
del excel # this line removed it from task manager in my case
ROJI
  • 131
  • 1
  • 5
4

What worked for me was making sure to de-reference any variables that you assigned along the way like so:

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

It worked with either Dispatch format.

Robert Criqui
  • 208
  • 3
  • 11
4

I have found that killing an Excel process through win32com is not always that reliable. These include excel.Application.Quit(), and del excel as suggested above.

I found the only way to make sure it is dead is to physically kill the EXCEL.EXE process:

import psutil

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

Downside is that this script will obviously kill all excel process currently running. IF you can live with this, then this is not a bad option.

Paul
  • 813
  • 11
  • 27
3

I have this in my files that use Excel:

self.excel.Application.Quit()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aaron S
  • 711
  • 2
  • 10
  • 23
  • 2
    and are you using DispatchEx, or Dispatch, and it really close or keeps running? because in fact when I close it seems to be closed but, it keeps alive when i see the task manager – sacabuche Aug 08 '12 at 17:42
  • Oops sorry about that. I'm using EnsureDispatch which might make the difference. It's totally closed afterwards. – Aaron S Aug 08 '12 at 18:54
2

Python should handle the lifecycle of the COM object. Just set excel = None. See this for reference:

# 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

LeanMan
  • 474
  • 1
  • 4
  • 18
2

To any newcomer from search engine:

In my trial, this answer says right Python should handle the lifecycle of the COM object... . Just set excel = None and the Excel.exe PID will be closed automatically now or after a while. If you still find the Excel.exe existing for a long time, call python gc collect.

import gc
gc.collect()
Kn.Bk
  • 21
  • 4
1

Most of the other answers leverage killing Excel entirely. Problem is, this will close any other instance of Excel that might have been opened.

To get the workbook you opened to actually close, you need to close and delete your workbook (return value from 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)

This can be wrapped up in a try ... except ... block to ensure that it gets closed even if there was an error. For example...

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)

If for whatever reason you REALLY need to kill Excel and close all open workbooks, then you'll need to force close all workbooks, quit the application and delete the excel object.

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)
Chris Collett
  • 1,074
  • 10
  • 15
0

After numerous failed attempts, this is the method that finally worked for me:

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()

I haven't tested this script with Workbooks but my understanding is that you'll also need to close and set them equal to None before excel.Application.Quit() is called.

Tyler
  • 161
  • 1
  • 11