| Home | Abstract | Prerequisites | Activities | Status |
I have recently been playing with Python and COM on Windows 2000. Whilst the
built-in help in PythonWin does go some way towards explaining the bizarre
details of COM and the various common interfaces one might find, it is really
necessary to go looking for concrete interface information from Microsoft, and
I found it necessary to look in the source code of the various examples
provided in the win32com package. My aim in this document is to
give enough information to help readers avoid having to do the same!
Perhaps the easiest way of obtaining Python for Windows platforms is to go
to ActiveState and download their
ActivePython package. You can alternatively visit the Python site, download
Python and then go looking for the win32all extensions. I just
did what was easiest, of course!
For the activites given below, you may need extra software such as Microsoft Outlook, although you probably won't be interested in playing with Outlook and Exchange if you don't have them.
Whilst the win32com package provides a number of ways of querying and accessing COM interfaces with few obvious differences between them (provided you know the interface), it is usually more convenient when experimenting (particularly in PythonWin with its attribute completion feature) to know what methods and properties are really available for a given interface. This is where the "COM Makepy utility", accessible from the "Tools" menu in PythonWin, is useful. By selecting a particular type library and building the Python interfaces using this tool, much more information becomes available in PythonWin, and casual investigation of object interfaces becomes much easier.
All client COM access starts with the following statement in your Python program:
import win32com.client
Typically, your program will then try and find an object to use. For example:
object = win32com.client.Dispatch("Outlook.Application")
This object will then refer to a "COM object" which can have methods invoked upon it and its attributes examined like any other Python object, although we may be "automating" a Windows application (such as Outlook) when we do this.
One of the supposedly irritating things about Microsoft Exchange Server is the way that it hides a lot of its internals and requires a fair amount of programming to let you get to things such as contact lists and address books. It's interesting to note that much advice is to be found on importing other systems' address books into Exchange but not as much on getting the addresses back out and into something else. (As if Microsoft think that you would want to, really!)
Anyway, this led me to write a small program to access Exchange's public address books through Outlook. If your organisation is running Exchange, you're probably required to run Outlook as well, and therefore automating Outlook is a valid strategy in accessing Exchange. One alternative for accessing certain types of information is to use the Active Directory Services API and/or LDAP, but I haven't been successful in that area.
Reminder: before starting the example, build the "Microsoft Outlook 9.0 Object Library" using the "COM Makepy utility".
The "interactive address book" program (which you can download) uses MAPI (Messaging API) within Outlook to access mail resources. The first thing it does is to obtain a reference to the Outlook application's automation object and to enter the MAPI namespace:
o = win32com.client.Dispatch("Outlook.Application")
ns = o.GetNamespace("MAPI")
What this really does is beyond my level of interest, but it does allow us to access the "folder" hierarchy accessible within Outlook. You can see this hierarchy yourself by opening Outlook manually and bringing up the folder menu (which typically says "Inbox" or "Outlook Today" or something).
The example program uses some fancy Python tricks to access the attributes of the "COM objects" it references, but in essence it looks up the folders and items available within each object:
# Refer to the folders within a given object... subobject = object.Folders # Refer to the items within a given object... subobject = object.Items
Now, how does one investigate the detail of each object? For example, how does one access the name of a folder? Firstly, it helps to know the interface that the object exposes, and this information can be found in two principal places:
Hopefully, however, the object that you are accessing is known well enough by PythonWin to permit some kind of attribute or method completion on it. You should only need to resort to the above when more detailed knowledge about a method or attribute is required. You can also try something like this:
dir(object.__class__)
The name of a folder can be accessed as follows:
object.Name # Where object refers to a folder.
Of course, there can be many folders or items in a folder. How do we traverse this collection? The recommended way is arguably to use Python's sequence index notation, but beware: the first element's index apparently is not standard and can be 0 or 1. In Outlook's object model, the first element in such sequences is indeed indexed by 1, resulting in the following observations:
len(object) gives 34 then the last element does
not have the index 33 - it has the index 34. Very un-Pythonic, we
might claim, but apparently there isn't anything that can really be done
about it.To access objects which behave like sequences, the following Python mechanisms can be used:
object[1] # The first element. len(object) # Return the number of elements.
The following mechanisms do not seem to work:
object[-1] # Should be the last element, but instead we get an exception. object[1:4] # Should slice the sequence, but instead we get an exception.
With the above information, the example program can access different levels
of folders, query items and extract addresses to a file. By using the
Parent attribute on any given folder, we can navigate upwards
through the hierarchy without needing to keep references to places we have
already explored.