简体   繁体   中英

How to properly initialize win32com.client.constants?

I'm writing a simple email filter to work upon Outlook incoming messages on Windows 10, and seek to code it up in Python using the win32com library, under Anaconda. I also seek to avoid using magic numbers for the "Inbox" as I see in other examples, and would rather use constants that should be defined under win32com.client.constants . But I'm running into simple errors that are surprising:

So, I concocted the following simple code, loosely based upon https://stackoverflow.com/a/65800130/257924 :

import sys
import win32com.client

try:
    outlookApp = win32com.client.Dispatch("Outlook.Application")
except:
    print("ERROR: Unable to load Outlook")
    sys.exit(1)

outlook = outlookApp.GetNamespace("MAPI")
ofContacts = outlook.GetDefaultFolder(win32com.client.constants.olFolderContacts)
print("ofContacts", type(ofContacts))
sys.exit(0) 

Running that under an Anaconda-based installer (Anaconda3 2022.10 (Python 3.9.13 64-bit)) on Windows 10 errors out with:

(base) c:\Temp>python testing.py
Traceback (most recent call last):
  File "c:\Temp\testing.py", line 11, in <module>
    ofContacts = outlook.GetDefaultFolder(win32com.client.constants.olFolderContacts)
  File "C:\Users\brentg\Anaconda3\lib\site-packages\win32com\client\__init__.py", line 231, in __getattr__
    raise AttributeError(a)
AttributeError: olFolderContacts

Further debugging indicates that the __dicts__ property is referenced by the __init__.py in the error message above. See excerpt of that class below. For some reason, that __dicts__ is an empty list:

class Constants:
    """A container for generated COM constants."""

    def __init__(self):
        self.__dicts__ = []  # A list of dictionaries

    def __getattr__(self, a):
        for d in self.__dicts__:
            if a in d:
                return d[a]
        raise AttributeError(a)


# And create an instance.
constants = Constants()

What is required to have win32com properly initialize that constants object?

The timestamps on the init .py file show 10/10/2021 in case that is relevant.

The short answer is to change:

outlookApp = win32com.client.Dispatch("Outlook.Application")

to

outlookApp = win32com.client.gencache.EnsureDispatch("Outlook.Application")

The longer answer is that win32com can work with COM interfaces in one of two ways: late- and early-binding .

With late-binding , your code knows nothing about the Dispatch interface ie. doesn't know which methods, properties or constants are available. When you call a method on the Dispatch interface, win32com doesn't know if that method exists or any parameters: it just sends what it is given and hopes for the best!

With early-binding , your code relies on previously-captured information about the Dispatch interface, taken from its Type Library. This information is used to create local Python wrappers for the interface which know all the methods and their parameters. At the same time it populates the Constants dictionary with any constants/enums contained in the Type Library.

win32com has a catch-all win32com.client.Dispatch() function which will try to use early-binding if the local wrapper files are present, otherwise will fall back to using late-binding. My problem with the package is that the caller doesn't always know what they are getting, as in the OP's case.

The alternative win32com.client.gencache.EnsureDispatch() function enforces early-binding and ensures any constants are available . If the local wrapper files are not available, they will be created (you might find them under %LOCALAPPDATA%\Temp\gen_py\xx\CLSID where xx is the Python version number, and CLSID is the GUID for the Type Library). Once these wrappers are created once then the generic win32com.client.Dispatch() will use these files.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM