Search code examples
pythoncomwin32com

How do I start a COM server? Code is in Python


I want to run Python code as a COM server. Eventually I want to run an RTD server available here. But first I want to know what exactly you have to do to getting any COM server running. So let's focus on this example.

class HelloWorld:
    _reg_clsid_ = "{7CC9F362-486D-11D1-BB48-0000E838A65F}"

    _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

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


if __name__=='__main__':

    import win32com.server.register
    win32com.server.register.UseCommandLine(HelloWorld)

Ok, this works in the way that there were no errors and server is registered, hence it is available in the HKEY_CLASSES_ROOT registry. But what can I do with this? Some say you have to compile a instance and have a .dll or .exe file. WHat else do I have to do?


Solution

  • Well, I ran your example. The registry key for the server is at:

    HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{7CC9F362-486D-11D1-BB48-0000E838A65F}
    

    It has two subkeys... one for LocalServer32 and one for InProcServer32

    I created a simple VBA macro in Excel:

    Sub d()
    Set obj = CreateObject("Python.TestServer")
    
    MsgBox obj.Hello("joe")
    End Sub
    

    Macro ran just fine. My version of Excel is 64-bit. I ran the macro and then fired up Task Manager while the message box was being displayed. I could see pythonw.exe running in the background.

    The only difference between my python script and yours is probably the name and also that I added a line to print to make sure I was executing the function:

    if __name__=='__main__':
    
        import win32com.server.register
        print("Going to register...")
        win32com.server.register.UseCommandLine(HelloWorld)
    

    When I ran the 64-bit csript.exe test, it worked... as expected... when I ran the 32-bit version it failed.

    I know why...sort of... The registry entry for InProcServer32 is pythoncom36.dll

    That's no good. It is an incomplete path. I tried modifying the path variable on my shell to add to one of the 3 places where the DLL existed on my system, but it didn't work. Also, tried coding the path in the InProcServer32. That didn't work.. kept saying it couldn't find the file.

    I ran procmon, and then I observerved that it couldn't load vcruntime140.dll. Found the directory under python where those files were, and added to my path. It got further along. If I cared enough, I might try more. Eventually using procmon, I could find all the problems. But you can do that.

    My simple solution was to rename the key InProcServer32 for the CLSID to be _InProcServer32. How does that work? Well, the system can't find InProcServer32 so it always uses LocalServer32--for 32-bit and 64-bit processes. If you need the speed of in process then you'd need to fix the problem by using procmon and being relentless until you solved all the File Not Found errors and such. But, if you don't need the speed of in process, then just using the LocalServer32 might solve the problem.

    Caveats I'm using an Anaconda distro that my employer limits access to and I can only install it from the employee store. YMMV.