Can't close Excel completely using win32com on Python
Asked Answered
C

10

31

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!!!
Convoluted answered 19/4, 2012 at 3:12 Comment(5)
Try adding the line excel.DisplayAlerts = False before the call to wbs.Open(...) and see if that helps.Communize
:/ not working, is a real pain excel and COMConvoluted
I find the way but no using COMConvoluted
The quicker, cleaner, easier answer is here: stackoverflow.com/a/18421814Generative
Force closing the Excel process is not a good idea, since the user might have other Excel files open that they do not want closed.Barney
S
14

Try this:

wbs.Close()
excel.Quit()
del excel # this line removed it from task manager in my case
Sonics answered 18/11, 2013 at 13:25 Comment(3)
del excel does not remove the Excel process from the task managerChrissy
Try changing wbs.Close() to wbs.Close(False) to close the workbook without saving.Modal
This is going to close all open workbooks, even ones your application didn't open. Try this answer instead.Barney
E
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.

Erde answered 19/8, 2020 at 17:13 Comment(0)
S
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.

Shuster answered 1/4, 2021 at 7:50 Comment(0)
P
3

I have this in my files that use Excel:

self.excel.Application.Quit()
Patmore answered 7/8, 2012 at 16:33 Comment(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 managerConvoluted
Oops sorry about that. I'm using EnsureDispatch which might make the difference. It's totally closed afterwards.Patmore
K
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

Kremenchug answered 9/9, 2020 at 6:6 Comment(0)
B
2

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)
Barney answered 16/3, 2022 at 15:30 Comment(0)
S
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()
Sought answered 1/4, 2022 at 3:13 Comment(1)
the only thing that works so far, thanks!Ultrastructure
C
2

In my approach, I obtain the Process ID (PID) immediately upon creating an instance. This PID serves as a crucial identifier, enabling me to terminate the process efficiently once I've completed my tasks. Instead of relying on the conventional Excel.Quit method, I implement a custom function that accepts the PID as input:

xl_app = win32.DispatchEx('Excel.Application')

_, v_pid = win32process.GetWindowThreadProcessId(xl_app.Hwnd)

Perform tasks...

close_instance_by_pid(v_pid)

The close_instance_by_pid function effectively manages the termination process:

import psutil
import logging

def close_instance_by_pid(pid):
    try:
        process = psutil.Process(pid)
        for child in process.children(recursive=True):
            child.kill()
        process.kill()
        logging.info(f"Excel instance with PID {pid} successfully closed.")
    except psutil.NoSuchProcess:
        logging.error(f"No process found with PID {pid}.")
    except Exception as e:
        logging.error(f"Error closing Excel instance with PID {pid}: {e}")
Cruzcruzado answered 26/4 at 9:42 Comment(0)
G
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.

Glutamate answered 27/3, 2023 at 9:23 Comment(0)
T
0

I guess very late to the party but I have tried all answers and some other methods as well, but every method failed at some point when it comes to already opened excel files not to close.

If an excel file is already opened, it should not close, but the excel application's instance must be closed/end from the Task Manager

So this Works Perfectly fine in my case, i wish the same for everyone's case in future use

def xel_to_pdf(xel, pdf):
    excel = client.DispatchEx("Excel.Application") #Can use client.Dispatch as well but that will cause a popup alert as the file is read-only everytime you open the saved excel file. So DispatchEx will save you from this heck
    excel.Interactive = False
    excel.Visible = False
    excel.DisplayAlerts = False

    sheets = excel.Workbooks.Open(xel, None, True)
    work_sheets = sheets.Worksheets('sheet1')
    work_sheets.ExportAsFixedFormat(0, pdf) #for example you want to convert excel to pdf
    for w_b in excel.Workbooks:
        if w_b.Name == xel:
            w_b.Close()
    excel.Quit()
    # excel.Application.Quit() will not remove excel instance from the Task-Manager, So use excel.Quit()

xel_to_pdf('excel_path.xlsx', 'PATH_of_pdf_name')

You can use Try Except for any Error Handling according to your needs...

Tyrant answered 29/1 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.