This document describes how to use the PythonCOM framework if you are a Python programmer. Before reading this, you may like to become familiar with the win32com package structure. For details on how to extend the core COM functionality, or for a description of how it all works, please see Python COM Implementation documentation.
Described here you will find
Client side COM support is implemented in the win32com.client package. This contains various modules to assist in working with client side objects.
Dynamic.py is a module which assists in using "dynamic" dispatch objects. Dynamic in this sense means the support is for objects created "on the fly", as opposed to objects that have a dedicated Python module supporting them (as would be created by the makepy module).
Dynamic.py defines two useful functions - Dispatch and DumbDispatch.
These are used to create objects on the fly, and provide a nice,
natural interface to the underlying object. However, restrictions
do apply. Python and COM clash in a number of areas, and although
all efforts are made, some gotchyas apply. These will be described
in detail later.
OK - lets jump straight in, and see how we use all this wonderful stuff to do something useful.
First we need to import the "ni" module, and the win32com.client.dynamic module.
>>> import ni, win32com.client.dynamic
Next, we create a COM object for the program identified by "Excel.Application" - ie, Excel itself.
>>> xl = win32com.client.dynamic.Dispatch("Excel.Application")
"xl" is now an instance of "Dispatch", which has an underlying COM object wrapped up inside it.
Excel itself is created invisible - to make Excel show itself, we need to set a property of the Excel application object. First, lets have a look at the property:
>>> xl.Visible
0
OK - not visible - lets set the property to True.
>>> xl.Visible = 1 # Excel instantly shows itself.
Notice that Excel is opened with no Worksheets opened. We will create a new workbook object
>>> xl.Workbooks().Add()
<COMObject <unknown>>
Notice that the Add() method returns a new workbook object. We don't need this object for this demonstration. Lets stick some text in the first cell of the new workbook.
>>> xl.Cells(1,1).Value = "Hello"
And we will see the word "Hello" appear in the top cell.
Note that the usage of this object is quite transparent. All properties work as you expect, as do method calls. Not too many surprises here. Unfortunately, this is because Microsoft Excel exposes type information for almost all of its objects. Python uses this type information to determine what are properties, what are methods, etc.
Some other objects are not as kind. More details on this to follow.
Below are listed some hacks that can be used for client side COM. By definition, these items are all implementation specific, and officially not supported. Use at your own risk, and expect them to change over time.
_print_details_() method
If dynamic.Dispatch is used, the objects have a _print_details_()
method available, which prints all relevant knowledge about an
object. In the example of Excel above, then all methods and properties
would be listed. For objects that do not expose type information,
_print_details_ may not list anything.
makepy.py is a tool which takes a "Type Information" file, and generates a Python source file. This Python source file is then used to interface to the COM object.
Server Side COM is the ability to create objects that expose an interface to other applications. The most common usage if this is to create IDispatch based interfaces - also known as "Automation Objects". Before you can use a server, it must be registered with COM.
Before jumping in to a sample, we need to describe how it all works.
Whenever a Python Server needs to be created, the C++ framework first instantiates a "policy" object. This "policy" object is the gate-keeper for the COM Server - it is responsible for creating the underlying Python object that is the server (ie, your object), and also for translating the underlying COM requests for the object.
All of the underlying COM functionality is handled by this policy object. For example, COM requires all methods and properties to have unique numeric ID's associated with them. The policy object manages the creation of these ID's for the underlying Python methods and attributes. Similarly, when the client whishes to call a method with ID 123, the policy object translates this back to the actual method, and makes the call.
This means there are very few requirements imposed on a Python object that wishes to be a COM server - almost all the details are handled by the policy object on its behalf. The addition of one single line of code to the class is often all that is needed.
It should be noted that the operation of the "policy" object can be dictated by the Python object - the policy object has many defaults, but its operation can always be dictated by the actual Python class.
Just to demonstrate this, below is a minimal COM server:
class MyTestServer:
_public_methods_ = ['Say'] # This is the extra line needed
to enable COM
def Say(self, message)
print message # This probably wont go anywhere useful!
The _public_methods_ attribute identifies the object as a valid COM server to the policy object. It uses this attribute to determine which methods to expose. The only other thing needed is registration of this server in the registry, and it will work.
The policy object has a few special attributes that define who the object is exposed to COM. The example above shows the _public_methods attribute, but this section describes all such attributes in detail.
Required list of strings, containing the names of all methods to be exposed to COM. It is possible this will be enhanced in the future (eg, possibly '*' will be recognised to say all methods, or some other ideas )
Optional list of strings containing all attribute names to be exposed, both for reading and writing. The attribute names must be valid instance variables.
Optional list of strings defining the name of attributes exposed read-only.
Optional list of IIDs exposed by this object. If this attribute
is missing, IID_IDispatch is assumed (ie, if not supplied, the
COM object will be created as a normal Automation object.
and actual instance attributes:
_dynamic_ : optional method
_value_ : optional attribute
_query_interface_ : optional method
_NewEnum : optional method
_Evaluate : optional method
Servers need to be able to provide exception information to their client. In some cases, it may be a simple return code (such as E_NOTIMPLEMENTED), but often it can contain much richer information, describing the error on detail, and even a help file and topic where more information can be found.
A scheme has been devised to allow Python Servers to support this functionality in a natural way. The Python server can take advantage of the fairly new Python feature of being able to raise a class instance as an exception. The COM framework will examine this exception, and look for certain known attributes. These attributes will be copied across to the COM exception, and passed back to the client.
To make working with exceptions easier, there is a helper module "exception.py", which defines a single class. An example of its usage would be:
raise Exception(desc="Must be a string",scode=winerror.E_INVALIDARG,helpfile="myhelp.hlp",...)