Reputation: 2849
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!!!
Upvotes: 31
Views: 42290
Reputation: 21
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)
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}")
Upvotes: 2
Reputation: 161
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...
Upvotes: 0
Reputation: 500
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.
Upvotes: 0
Reputation: 1279
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)
Upvotes: 3
Reputation: 982
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.
Upvotes: 4
Reputation: 21
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()
Upvotes: 2
Reputation: 524
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
Upvotes: 2
Reputation: 218
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.
Upvotes: 4
Reputation: 151
Try this:
wbs.Close()
excel.Quit()
del excel # this line removed it from task manager in my case
Upvotes: 14
Reputation: 731
I have this in my files that use Excel:
self.excel.Application.Quit()
Upvotes: 3