Search code examples
pythonwin32compywinautocomtypespythoncom

Importing pywinauto (or comtypes) clobbers existing COM object


Creating / getting a COM object using win32com.client.GetActiveObject and then import pywintypes results in an AttributeError upon each call to the original COM object.

Code to reproduce

import win32com.client
catia_com = win32com.client.GetActiveObject('CATIA.Application')

# then later when needed, do some pywinauto stuff
import pywinauto.application

# back to working directly on catia com object
print(catia.caption)  # raises Attribute error from within win32com.client

Why is this happening, and how can it be fixed?


Solution

  • I was able to find a solution to this which I'd like to share, but I'd also like to hear feedback on my understanding and what is the appropriate dependency where this should be addressed to prevent others from having same issue.

    Solution

    Add import comtypes before calling win32com.client.

    Example

    import comtypes
    import win32com.client
    catia_com = win32com.client.GetActiveObject('CATIA.Application')
    
    # then later when needed, do some pywinauto stuff
    import pywinauto.application
    
    # back to working directly on catia com object
    print(catia.caption)  # it works!
    

    Cause and explanation (as best as I could determine)

    TLDR

    import comtypes calls CoInitializeEx with flag COINIT_MULTITHREADED which happens to override the mode set by win32com.client.GetActiveObject.

    Full explanation

    I came to this conclusion because when commenting out pythoncom.CoUninitialize() (I was worried that it was killing the COM object returned from win32com.client.GetActiveObject) in pywinauto.__init__ and reruning the code the following error was raised from comtypes.__init__:

    OSError: [WinError -2147417850] Cannot change thread mode after it is set
    

    I know that comtypes.__init__ handels choosing the default flags for CoInitializeEx and couldn't deduce how the win32com lib is but figures it is also probably checking for existing flags, so I added import comtypes.

    Where I'm still fuzzy

    1. How do you determine what the concurrency model flags are for win32com?
    2. How do you specify the concurrency model flags when calling win32com.client.GetActiveObject?
    3. Why exactly does the solution work... calling pythoncom.CoInitializeEx(0x0) right before calling win32com.client.GetActiveObject doesn't work!

    If I can get the above answered I can make a recommendation to win32com to set sys.coinit_flags (this is where comtypes.__init__ is checking).

    Rescources