Search code examples
python-3.xexcelpywin32win32com

How to check if a file is open (e.g., Excel Workbook) or if its been closed by the user using pywin32 COM library?


There are a lot of examples of VBA and C# code that checks if a workbook is open using the Win API for COM objects, but there is none for python using the pywin32. I tried to use functions like GetObject and GetItem but they are not without their challenges. Please see code snippet below with my method for trying to check if the user has closed the workbook. It opens up a workbook and then keeps it open for ten seconds. If the user closes, insanity ensues. Does anyone know the proper way to write this code? Thanks!

# Open an excel, check if its open, wait until user closes or close it after ten seconds
def wait_until_time_or_user_close(excel, gencache=False):
    excel = win32.gencache.EnsureDispatch('Excel.Application')
    excel.Visible = True
    excel.DisplayAlerts = False
    wb_open = True
    try:
        wb = excel.Workbooks.Add()  # create new workbook
        name = ''.join(wb.Name)     # create a new string copy
                                    #  - needed because if user closes
                                    #    wb, then wb doesn't exist and
                                    #    wb.Name throws an error. 
        
        seconds = 0
        xl = None
        while(wb_open and seconds < 10):
            xl = win32.GetObject(name)
            # excel.Workbooks.getItem(name);  # produces AttributeError: 
                    # '<win32com.gen_py.Microsoft Excel 16.0 Object Library.Workbooks instance at 0x1548081920544>' 
                    # object has no attribute 'getItem' (or GetItem)
                    # Source: https://stackoverflow.com/questions/3156676/checking-if-an-excel-workbook-is-open
            wb_open = xl is not None

            # give user some time to interact
            import time
            time.sleep(1)
            seconds += 1
            
    finally:
        if wb_open:
            print("wb closed")
            wb.Close(False) # True=save the workbook, False=don't save workbook
        excel.Application.Quit()
excel = None
wait_until_time_or_user_close(excel)

Also, does anyone have any recommendations on how to find out different functions and attributes available to a COM object? The pywin32 documentation says the following:

How do I know which methods and properties are available?
Good question. This is hard! You need to use the documentation with the products, or possibly a COM browser. Note however that COM browsers typically rely on these objects registering themselves in certain ways, and many objects to not do this. You are just expected to know.

It says you are just expected to know... well if that is the case where do I go to know this stuff????

One can see the documentation here: http://timgolden.me.uk/pywin32-docs/html/com/win32com/HTML/QuickStartClientCom.html


Solution

  • Here is one way to solve the problem. Add a second try/catch statement. If there are better ways to do this - please recommend them. Also, the second question still stands on the best methods to find out about the Win32 API.

    # Open an excel, check if its open, wait until user closes or close it after ten seconds
    def wait_until_time_or_user_close(excel, gencache=False):
        excel = win32.gencache.EnsureDispatch('Excel.Application')
        excel.Visible = True
        excel.DisplayAlerts = False
        wb_open = True
        try:
            wb = excel.Workbooks.Add()  # create new workbook
            seconds = 0
            xl = None
            while(wb_open and seconds < 10):
                try:
                    xl = win32.GetObject(wb.Name)        
                except:
                    wb_open = xl is not None
    
                # give user some time to interact
                import time
                time.sleep(1)
                seconds += 1
                
        finally:
            if wb_open:
                print("wb closed")
                wb.Close(False) # True=save the workbook, False=don't save workbook
            excel.Application.Quit()
    excel = None
    wait_until_time_or_user_close(excel)