Distributing COM servers (Was: Re: How to distribute Python COM applications?)

Thomas Heller thomas.heller at ion-tof.com
Mon May 21 03:28:14 EDT 2001

"Gordon McMillan" <gmcm at hypernet.com> wrote in message news:90A7E7E3Fgmcmhypernetcom at
> hungjunglu at yahoo.com wrote in <mailman.990048145.9953.python-
> list at python.org>:
> [...]
> >So far I have succeeded in distributing Python COM client
> >applications.
> >
> >It's the Python COM server part that is complicated. I know py2exe or
> >Gordon installer are not enough, but besides that, more registry
> >tweaking and DLL handling are needed.
> Actually, it's more a problem with the arrangement of responsibilities.
> Your typical Python COM server defines a class and has some register /
> unregister logic under the "if __name__ == '__main__':" code. So it's both
> a script (when registering / unregistering) and a module (when attached
> through COM). If you just freeze your class, all you'll get is the register
> / unregister code, and it won't create the right entries.
> There *is* a script which runs (when you use out-of-process): it's
> win32com/server/localserver.py. As proof of concept, I hacked up
> localserver.py so that I got localserver.exe which could serve up my COM
> class. By manually tweaking the registry, I got it working (out-of-
> process). So I know this stuff is possible, it's just that generalizing and
> automating it is a considerable amount of work.

It's not very complicated, and you can achieve it _without_ hacking
localserver.py and without manually tweaking the registry.
Here are the tricks:
1. Detect if the script is running as py2exe-packed exe. py2exe
installs it's importers in the sys module as 'sys.importers'.
A similar trick should exist for Gordon's installer...

2. Set the 'frozen' attribute in the pythoncom module,
specify a _reg_class_spec_ attribute in the COM server class.

3. Run the 'localserver' module manually in the
'if __name__ == "__main__"' branch.

Attached is a simple COM server python script. If you play with it,
be sure to unregister and register it completely after switching
between the 'standard way' and when using the py2exe-packed way.

To register the Python script use:
'python testCOMserver.py --unregister' and then
'python testCOMserver.py --register'.

To build the executable:
'python setup.py py2exe'

To register the executable as COM server:
'cd dist\testCOMserver'
'testCOMserver.exe --unregister'
'testCOMserver.exe --register'


import sys
import pythoncom

if hasattr(sys, 'importers'):
    # we are running as py2exe-packed executable
    pythoncom.frozen = 1

class HelloWorld:
    _reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
    if hasattr(sys, 'importers'):
        # In the py2exe-packed version, specify the module.class
        # to use. In the python script version, python is able
        # to figure it out itself.
        _reg_class_spec_ = "__main__.HelloWorld"
    _reg_clsid_ = "{B83DD222-7750-413D-A9AD-01B37021B24B}"
    _reg_desc_ = "Python Test COM Server"
    _reg_progid_ = "Python.TestServer"
    _public_methods_ = ['Hello']
    _public_attrs_ = ['softspace', 'noCalls']
    _readonly_attrs_ = ['noCalls']
    def __init__(self):
        self.softspace = 1
        self.noCalls = 0
    # __init__()

    def Hello(self, who):
        self.noCalls = self.noCalls + 1
        # insert "softspace" number of spaces
        return "Hello" + " " * self.softspace + str(who)

if __name__ == '__main__':
    if hasattr(sys, 'importers'):
        # running as packed executable.
        if '--register' in sys.argv[1:] \
           or '--unregister' in sys.argv[1:]:
            # --register and --unregister work as usual
            import win32com.server.register
            # start the server.
            from win32com.server import localserver
        import win32com.server.register

This is the setup script you can use to build an executable
with py2exe:

from distutils.core import setup
import py2exe


More information about the Python-list mailing list