5—
Introduction to COM

In this chapter, we provide an introduction to using the Microsoft Component Object Model (COM) from Python. We will present enough introductory material so you can understand the key concepts and some simple examples that use Python and COM.

COM is a technique for using or publishing objects, regardless of the language they were implemented in. Any COM-aware environment uses a COM object in a consistent and reliable way; you need to know nothing about how the object is implemented.

COM has grown from humble beginnings. Its lineage can be traced through the Object Linking and Embedding (OLE) technology that first appeared in the Microsoft Windows 3.x family. The first version of Visual Basic included a technology known as Visual Basic Extensions, or VBXs. Microsoft started merging these technologies into OLE2, which was soon renamed COM. Since then, the Microsoft marketing machine has begun to refer to the technology as ActiveX, although COM remains the mnemonic of choice in the Windows development world. Recently, COM has been extended with the ability to distribute objects across a network, and these extensions are known as Distributed COM, or DCOM. Over time, the term DCOM will slowly vanish as the capabilities it offers are considered a standard part of COM. Microsoft is already hard at work on the next major revision of COM, currently known as COM+. Although COM+ isn't discussed in this book, all the existing COM concepts will remain for the foreseeable future.

Throughout the rest of the book, we present a number of examples that use Python and COM, so the intent of this chapter is to give you the groundwork to follow the samples. In Chapter 12, Advanced Python and COM, we expand on all these concepts, explaining the Python COM support in greater detail.

What It Is

COM is a technology from Microsoft that allows objects to communicate without the need for either object to know any details about the other, even the language it's implemented in.

At the lowest level, COM deals with interfaces and objects and makes a clear distinction between an object's interface and its implementation. The interface defines how an object is used, but the implementation of the interface is up to the object.

COM defines many interfaces but doesn't provide implementations for many of these interfaces. One commonly used interface, IDispatch, allows COM objects to be used from a scripting environment, such as Visual Basic or Python. Although COM has defined the interface for IDispatch, it's the responsibility of the COM objects themselves to implement this interface, and exactly how they implement it depends on the object model or functionality the COM object is trying to expose.

Objects that implement the IDispatch interface are known as automation objects. The rest of this chapter shows how to use automation objects from Python, and then we show how to create a COM object in Python and call from Visual Basic. Chapter 12 covers all these details in greater depth.

Using COM Objects from Python

When people discuss COM, they are often talking about only one side of COM—using automation objects. Automation objects are objects that expose a programmable interface that can be used by another program or environment. Examples of automation objects are Microsoft Office, the Netscape browser, or programs you write yourself in any language, such as Visual Basic, Python, Delphi, C++, and so forth.

Information about COM objects is stored in the Windows registry. Details about the object's class are stored, so that when that particular object needs to be created, the correct class is located and used. Although the term "class" doesn't refer to a Python (or C++) class, the concept is identical: the class defines the implementation, and the object is an instance of the class. Classes are registered with a unique (but complex) class ID (CLSID) and a friendly (but not guaranteed unique) program ID (ProgID). The CLSID is a globally unique identifier (GUID), as discussed later in this chapter, while the ProgID for an object is a short string that names the object and typically creates an instance of the object. For example, Microsoft Excel defines its ProgID as Excel.Application, Microsoft Word defines Word.Application, and so forth.

Python programs use the win32com.client.Dispatch() method to create COM objects from a ProgID or CLSID. For example, you could use this code to create an Excel object:

>>> import win32com.client
>>>xl = win32com.client.Dispatch("Excel.Application")
>>>

or to create a Microsoft Word object:

>>> import win32com.client
>>> wd = win32com.client.Dispatch("Word.Application")
>>>

So what to do with these objects? One of COM's greatest strengths is also one of its greatest weaknesses. Each COM object can define its own object model, that is, the methods and properties the object exposes to allow it to perform its task. The problem with this approach is that many COM objects present a unique object model, and if you learn how to use Microsoft Office using COM, the next COM object you need to use could define a different model. Microsoft is addressing this issue in its own products by attempting to define a similar object model across applications: the interface to Microsoft Excel is similar to the interface for Microsoft Word. However, the COM objects you need may present a completely different interface. The only solution to this problem is documentation; you must locate and read the documentation on the object model for the COM object you wish to use.

owl.gif When you install Microsoft Office, the documentation for the COM object model isn't installed by default During the installation process, you should select each product from the Installation Options and check the Help options for that product. If you have already installed Microsoft Office, you can run the setup program again and add these Help components to your installation.

If you view the documentation for Microsoft Office, notice that both Excel. Application and Word.Application have a Visible property. Let's look at this property for Microsoft Excel:

>>> xl.Visible
0
>>>

Excel isn't visible, explaining why you can't see an instance of Excel running on your PC. (It's there, though!)

Let's set the Visible property to true

>>> xl.Visible = 1
>>>

Excel now appears on the display. If you try the same thing with the Microsoft Word object, you get the same results:

>>> wd.Visible
>>> 0
>>> wd.Visible = 1
>>>

Python manages COM lifetimes automatically for you; when your xl variable is no longer used, Excel automatically closes. In Python, the simplest way to remove this variable is to assign it to another value. If you use the following code, notice that Excel vanishes from the screen; it knows there are no longer any programs referring to it:

>>> xl = None
>>>

For more information on using COM objects from Python, please see Chapter 12.

Implementing COM Objects with Python

In this section, we discuss how to implement COM objects using Python and a small sample of such an object. We also present some Visual Basic code that uses our Python implemented object.

For this demonstration, you'll write a simple COM object that supports a number of string operations. As Visual Basic is somewhat lacking in the string-processing department where Python excels, it's a good candidate. The sample provides a COM object with a single method, SplitString(). This method has semantics identical to the standard Python function string.split(); the first argument is a string to split, and the second optional argument is a string holding the character to use to make the split. As you have no doubt guessed, the method won't do much more than call the Python string.split() function.

There are two steps to implement COM objects in Python:

?Define a Python class with the methods and properties you wish to expose.

?Annotate the Python class with special attributes required by the PythonCOM framework to expose the Python class as a COM object. These annotations include information such as the objects ProgID, CLSID, and so forth.

The following code shows a small COM server written in Python:

# SimpleCOMServer.py - A sample COM server - almost as small as they come!
#
# We expose a single method in a Python COM object. class PythonUtilities:
    _public_methods_ = [ 'SplitString' ]
    _reg_progid_ = "PythonDemos.Utilities"
    # NEVER copy the following ID

    # Use"print pythoncom.CreateGuid()" to make a new one.
    _reg_clsid_ = "{41E24E95-D45A-11D2-852C-204C4F4F5020}"

    def SplitString(self, val, item=None):
        import string
        if item != None: item = str(item)
        return string.split(str(val), item)

# Add code so that when this script is run by
# Python.exe,.it self-registers.
if__name__=='__main__':
    print "Registering COM server?quot;
    import win32com.server.register
    win32com.server.register.UseCommandLine(PythonUtilities)

The bulk of the class definition is taken up by the special attributes:

_public_methods_
A list of all methods in the object that are to be exposed via COM; the sample exposes only one method, SplitString.

_reg_progid_
The ProgID for the new object, that is, the name that the users of this object must use to create the object.

_reg_clsid_
The unique CLSID for the object. As noted in the source code, you must never copy these IDs, but create new ones using pythoncom.CreateGuid().

Full details of these and other possible attributes can be found in Chapter 12.

The SplitString() method is quite simple: it mirrors the behavior of the Python string.split() function. A complication is that COM passes all strings as Unicode characters, so you must convert them to Python strings using the str() function. Note that in Python 1.6, it's expected that the string and Unicode types will be unified allowing the explicit conversions to be removed.

The only thing remaining is to register the object with COM. As the comments in the code imply, you can do this by executing the code as a normal Python script. The easiest way to do this is to open the source file in PythonWin and use the Run command from the File menu. After running the script, the PythonWin interactive window should display:

Registering COM server?BR> Registered: PythonDemos.Utilities

Finally, let's test the COM object. Use Visual Basic for Applications, which ships with both Microsoft Office and Microsoft Excel, and perform the following steps:

1. Start Microsoft Word or Microsoft Excel.

2. Press ALT-F8 to display the macros dialog.

3. Enter a name for the macro (e.g., TestPython) and select Create.

4. The Visual Basic editor is displayed. In the editor, enter the following code:

Set PythonUtils = CreateObject("PythonDemos.Utilities")
response = PythonUtils.SplitString("Hello from VB")
for each Item in response
  MsgBox Item
next

Your display should look something like Figure 5-1

0070-01.gif
Figure 5-1.
Visual Basic for Applications code that uses the sample COM sever

Now run this code by pressing the F5 key. If all goes well, you should see three message boxes. The first one is shown in Figure 5-2.

0070-02.gif
Figure 5-2.
First of three message boxes displayed by the VB code

Just to be complete and help keep your registry clean, unregister your sample COM server. You do this by following the same process that registered the server, except specify --unregister as an argument to your script. A message is printed saying the object is unregistered.

Globally Unique Identifiers

As mentioned earlier in the chapter, a COM object registers itself with a unique identifier. Whenever COM needs a truly unique identifier, it uses a globally unique identifier or GUID. A GUID is a 128-bit number, generated using a complex algorithm and the unique ID burnt into a computer's network interface card, which makes it statistically improbable the same number will ever be generated twice.

New GUIDs can be created from Python code using the following code:

>>> import pythoncom
>>> print pythoncom.CreateGuid()
{FA21CDC1-381F-11D3-8559-204C4F4F5020}

Python prints the GUID using the standard hexadecimal representation inside braces, and this same format is used everywhere an ASCII representation of a GUID is required.

GUIDs are used for a variety of purposes in COM; we have already discussed how they function as CLSIDs to uniquely identify object implementations, and in later chapters you'll see them put to use in a variety of different ways.

Conclusion

In this chapter, we presented a quick introduction to using Python and COM. We covered:

?Some key fundamental COM concepts to provide a context for the Python discussions.

?Using COM objects from Python and a quick example using Microsoft Excel or Microsoft Word.

?Implementation of COM objects in Python so they can be used by other COMaware environments or languages. We demonstrated a COM object using Visual Basic for Applications.

?The concept of the COM GUID to help you understand how those strange looking strings are used.

We use the concepts introduced here throughout the book. In Chapter 12 these concepts are described in far greater detail.


Back