COM server threads/unloading
Mark Hammond
mhammond at skippinet.com.au
Wed Jun 26 22:35:21 EDT 2002
There are a couple of things you should:
* Ensure CoUninitialize() is called for each thread (you would have
needed to call CoInitialize[Ex] manually for these threads.
* Call CoUninitialize() on the main thread. You did not need to call
* Ensure pythoncom._GetInterfaceCount() and pythoncom._GetGatewayCount()
both return zero. If not, your Python program still has a COM
reference, and this should be nuked.
As far as I know, if all these conditions are met everytihing will
unload correctly.
Mark.
Uncle Enzo wrote:
> Hello,
>
> I have noticed that my multithreaded COM servers are not unloading
> when their clients have unloaded.
>
> To reproduce this in a simple case, I am using the py2exe COM server
> recipe. Running the server example with the client code below results
> in the testserver.exe loading after the dispatch call, and unloading
> after the server reference is set to None.
>
> Then I added the googlelogger import statement and the two lines
> initializing and calling the log method. The logger that has two
> threads. Using the same client code results in the testserver.exe
> loading after the dispatch call, but it does not unload after the
> server reference is set to None, nor does it unload after the client
> terminates.
>
> What is the proper way to get a multithreaded python COM server to
> unload when there are no external references to it?
>
> Thanks in advance,
>
> Enzo
>
> ps, comments about my logger design are welcome, but I'm mostly
> interested in the threading issues :)
>
> -----------------------------
>
> Python 2.2.1 (#34, Apr 9 2002, 19:34:33) [MSC 32 bit (Intel)] on
> win32
> Type "help", "copyright", "credits" or "license" for more information.
>
>>>>import win32com.client
>>>>server = win32com.client.Dispatch("Python.TestServer")
>>>>print server.Hello('enzo')
>>>
> Hello enzo
>
>>>>server = None
>>>>^Z
>>>
>
> ----------------------------
> file: testcomserver.py
>
> import googlelogger
> 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_ = "{592C15CE-894D-11D6-9A35-0050DA05A582}"
> _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
> self.mylogger = googlelogger.getLogger()
> # __init__()
>
> def Hello(self, who):
> self.noCalls = self.noCalls + 1
> # insert "softspace" number of spaces
> self.mylogger.log('caller', str(who))
> 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
> win32com.server.register.UseCommandLine(HelloWorld)
> else:
> # start the server.
> from win32com.server import localserver
> localserver.main()
> else:
> import win32com.server.register
> win32com.server.register.UseCommandLine(HelloWorld)
>
> ----------------------
> file: setup.py
>
> from distutils.core import setup
> import py2exe
>
> setup(name='testCOMserver',
> scripts=['testCOMserver.py'])
>
> ------------------------
> file: googlelogger.py
>
> import threading
> import Queue
> import sys
> import pavlnchcfg
> import time
> import win32com.client
>
> # This thread waits for ("name", "value") items to appear in the
> queue.
> # It then checks for a log named "name", creates it if it doesn't
> exist,
> # and appends the message to that log, with a timestamp.
> class Writer(threading.Thread):
> def setLogger(self, logger):
> #print "setting self._logger to ", logger
> self._logger = logger
>
> def run(self):
> print "starting Writer.run thread"
> while 1 :
> # blocks until there are items in the queue
> event = self._logger._logeventqueue.get(1)
> #get returns a tuple, (name, message)
> if event[0] == 'QUIT': #magic quit event
> print "logger.Writer.run: got magic event: returning"
> return
>
> logname = event[0]
> message = event[1]
> try:
> filename = self._logger._dir + '\\' + logname + ".txt"
> logfile = open(filename, 'a')
> logfile.write(time.asctime() + " " + message + '\n')
> logfile.flush()
> logfile.close()
> print "logger.Writer.run: logged ", message, " to ",
> logname
> except:
> print "logger.Writer.run: with to log: ", logname, "
> failed with message: ", message
>
> class Logger:
> # class variables
> _logeventqueue = Queue.Queue() #one thread safe multiple user
> queue
> _dir = ""
>
> def __init__(self):
> try:
> Logger._dir="C:\\" # for our example
> print "logger.Logger.init: got log directory",
> Logger._dir, "from config"
> except:
> Logger._dir = "C:\\"
> print "logger.Logger.init: No log directory specified in
> config, _dir set to C:\\"
>
> writer = Writer()
> writer.setName("writerthread")
> writer.setLogger(self)
> writer.start()
>
> def log(self, logname, message):
> event = logname, message #making the event tuple
> # event tuple is tuple[0] = name, tuple[1] = event
> Logger._logeventqueue.put(event)
>
>
> # factory pattern as module instance
> _loggerInstance = None
>
> def getLogger():
> import logger
> if logger._loggerInstance == None:
> logger._loggerInstance = Logger()
> return logger._loggerInstance
> else:
> return logger._loggerInstance
>
> if __name__ == '__main__': #test main
> import logger
> print "starting main"
> l = getLogger()
> print l
> l.log("DEBUG", "l, debug message 1")
> l.log("USAGE", "l, usagestat 1")
> l.log("DEBUG", "l, debug message 2")
> lg = getLogger()
> print lg
> lg.log("DEBUG", "lg, debug message 1")
> lg.log("USAGE", "lg, usagestat 1")
> lg.log("DEBUG", "lg, debug message 2")
> l.log("QUIT", "QUIT")
> print "done"
More information about the Python-list
mailing list