Home | People | COM | Emulation |
---|---|---|---|
Python |
I have been playing withPython andCOM on Windows 2000. Whilst the built-in help in PythonWin does go some waytowards explaining the bizarre details of COM and the various commoninterfaces one might find, it is really necessary to go looking for concreteinterface information. There are a few sources of such information, and theaim of this document is to help readers use such sources, or even to helpthem avoid the need to use such sources.
Perhaps the easiest way of obtaining Python for Windows platforms is to gotoActiveState and download theirActivePython package. You can alternatively visit the Python site, downloadPython (see thePython support forWindows page) and then go looking for thewin32all
extensions (see Mark Hammond'sPython for WindowsExtensions page for details). I just did what was easiest, of course!
For the activities given below, you may need extra software such asMicrosoft Outlook, although you probably won't be interested in playing withOutlook and Exchange if you don't have them.
One of the supposedly irritating things about Microsoft Exchange Server isthe way that it hides a lot of its internals and requires a fair amount ofprogramming 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 othersystems' address books into Exchange but not as much on getting the addressesback out and into something else. Fortunately, Microsoft provide enough of anAPI to become its own "undoing", since this API provides a reasonablemechanism to access the internals and to extract the locked-ininformation.
Anyway, this led me to write a small program to access Exchange's messagelists, contacts and appointments through Outlook. If your organisation isrunning Exchange, you're probably required to run Outlook as well, andtherefore automating Outlook is a valid strategy in accessing Exchange. Onealternative for accessing certain types of information is to use the ActiveDirectory Services API and/or LDAP, but I haven't been successful in thatarea.
Note: Before starting this application, build the"Microsoft Outlook 9.0 Object Library" using the "COM Makepy utility" (see"Initialising Type Libraries").
The Outlook Explorer program (which you candownload) uses MAPI (Messaging API)within Outlook to access mail resources. You can navigate the objecthierarchy using a simple command-line interface, and extract information toexisting directories in the filesystem.
Whilst the win32com package provides a number of ways of querying andaccessing COM interfaces with few obvious differences between them (providedyou know the interface), it is usually more convenient when experimenting(particularly in PythonWin with its attribute completion feature) to knowwhat methods and properties are really available for a given interface. Thisis where the "COM Makepy utility", accessible from the "Tools" menu inPythonWin, is useful. By selecting a particular type library and building thePython interfaces using this tool, much more information becomes available inPythonWin, and casual investigation of object interfaces becomes mucheasier.
All client COM access starts with the following statement in your Pythonprogram:
import win32com.client
Typically, your program will then try and find an object to use. Forexample:
object = win32com.client.Dispatch("Outlook.Application")
This object will then refer to a "COM object" which can have methodsinvoked upon it and its attributes examined like any other Python object,although we may be "automating" a Windows application (such as Outlook) whenwe do this.
Once we have a reference to an object which is "automating" anapplication, the next thing to do is to find the root of the object hierarchywhere all the interesting information is stored. With the Outlookapplication's automation object, the easiest way to achieve this is to enterthe MAPI namespace:
ns = object.GetNamespace("MAPI")
This allows us to access the "folder" hierarchy accessible within Outlook.You can see this hierarchy yourself by opening Outlook manually and bringingup the folder menu (which typically says "Inbox" or "Outlook Today" orsomething).
Thens
object can now be treated like a normal Python object,with special attributes leading to other parts of the object hierarchy.
The Outlook Explorer program uses some fancy Python tricks to access theattributes of the "COM objects" it references, but in essence it looks up thefolders and items available within each object:
# Refer to the folders within a given object...ns_list = ns.Folders# Refer to the items within a given object...ns_list = ns.Items
Now, how does one investigate the detail of each object? For example, howdoes one access the name of a folder? Firstly, it helps to know the interfacethat the object exposes, and this information can be found in severalplaces:
win32com/gen_py
directory within your Python distribution, or invoke an erroneous method or access a non-existent attribute on an object with that interface in order to see what the name of the interface file is.Hopefully, however, the object that you are accessing is known well enoughby PythonWin to permit some kind of attribute or method completion on it. Youshould only need to resort to the above when more detailed knowledge about amethod 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 wetraverse this collection? The recommended way is arguably to use Python'ssequence index notation, but beware: the first element's index apparently isnot standard and can be 0or 1. In Outlook's object model, the firstelement in such sequences is indeed indexed by 1, resulting in the followingobservations:
len(object)
gives34
then the last element doesnot 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 Pythonmechanisms 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 differentlevels of folders, query items and extract information. By using theParent
attribute on any given folder, we can navigate upwardsthrough the hierarchy without needing to keep references to places we havealready explored.