Search code examples
pythonexcelironpythonpywin32win32com

Cancel Excel's close event using python and win32com


Currently I try to cancel Excel's close event using Python and win32com. I have already managed to handle this issue with IronPython some month ago. But for further purposes of my companies department this should also be able with Python. Followed you will see two snippets. The first will contain the working IronPython Code

import clr
clr.AddReference("Microsoft.Office.Interop.Excel")
clr.AddReference("System.Windows.Forms")
from Microsoft.Office.Interop import Excel
from System.Windows.Forms import Form, Application, MessageBox, MessageBoxButtons, MessageBoxIcon, DialogResult

class CloseEventTry(Form):
    def __init__(self):
        excel = Excel.ApplicationClass()
        excel.Visible = True 
        excel.DisplayAlerts = False
        self.workbooks = excel.Workbooks.Add()
        self.Text = "Dummy GUI Window"      
        #link "BeforeCloseEvent" to the "beforeClose" method
        self.workbooks.BeforeClose +=Excel.WorkbookEvents_BeforeCloseEventHandler(self.beforeClose)

    def beforeClose(self, cancel):
        print type(cancel)  #Type: 'StrongBox[bool]
        choice = MessageBox.Show("Close Excel", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Information)
        if choice == DialogResult.Yes:
            cancel.Value = False    #do't cancel the close action
            self.Close()
        elif choice == DialogResult.No:
            cancel.Value = True     #prevent excel from closing 

Application.Run(CloseEventTry())

The second one will contain the version with Python and win32com. This one is based on my IronPython snippet and the sample of that link

https://win32com.goermezer.de/microsoft/office/events-in-microsoft-word-and-excel.html

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form, Application, MessageBox, MessageBoxButtons, MessageBoxIcon, DialogResult
import win32com.client as win32

#workbook event handler class. Needed according to this example https://win32com.goermezer.de/microsoft/office/events-in-microsoft-word-and-excel.html
class WorkBookEvents(object):
    def OnBeforeClose(self, cancel):
        print(type(cancel))  #Type: class 'bool'
        choice = MessageBox.Show("Close Excel", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Information)
        if choice == DialogResult.Yes:
            #do't cancel the close action => raises AttributeError: 'bool' object has no attribute 'Value' Exception
            cancel.Value = False    
            self.Close()
        elif choice == DialogResult.No:
            #prevent excel from closing => raises AttributeError: 'bool' object has no attribute 'Value' Exception
            cancel.Value = True     

class CloseEventTry(Form):
    def __init__(self):
        excel = win32.DispatchEx('Excel.Application')
        excel.Visible = True # makes the Excel application visible to the user
        excel.DisplayAlerts = False
        self.Text = "Dummy GUI Window"  
        self.workbooks = excel.Workbooks.Add()
        #define event handler according to this example https://win32com.goermezer.de/microsoft/office/events-in-microsoft-word-and-excel.html
        self.workbooks = win32.DispatchWithEvents(self.workbooks, WorkBookEvents)

Application.Run(CloseEventTry())

As you will see I could connect to the "OnBeforeClose" event, but cannot cancel the close event as I've done it with the IronPython version. As mentioned in the last code snippet's comment, the Python version raises an AttributeError exception. Further you can also see, that the types of the needed "cancel" variable of the event handlers have two different types. In the IronPython version its a "StrongBox[bool]". On the other hand the Python version's type is a common "class 'bool'" type (which explains the exception). Thats way I tried to just type

cancel = True #prevent excel from closing

But using this way, excel closes anyway. I also did some research but was not able to find a solution for this issue. My Assumption is that there is some kind of wrapper needed?


Solution

  • you can achieve the same behaviour as the IronPython version by returning the desired cancel value, e.g.

    return True
    

    in case you want to abort the Close event.

    Regards