0001 """Testing pasing object between multiple COM threads 0002 0003 Uses standard COM marshalling to pass objects between threads. Even 0004 though Python generally seems to work when you just pass COM objects 0005 between threads, it shouldnt. 0006 0007 This shows the "correct" way to do it. 0008 0009 It shows that although we create new threads to use the Python.Interpreter, 0010 COM marshalls back all calls to that object to the main Python thread, 0011 which must be running a message loop (as this sample does). 0012 0013 When this test is run in "free threaded" mode (at this stage, you must 0014 manually mark the COM objects as "ThreadingModel=Free", or run from a 0015 service which has marked itself as free-threaded), then no marshalling 0016 is done, and the Python.Interpreter object start doing the "expected" thing 0017 - ie, it reports being on the same thread as its caller! 0018 0019 Python.exe needs a good way to mark itself as FreeThreaded - at the moment 0020 this is a pain in the but! 0021 0022 """ 0023 0024 import thread, traceback 0025 import win32com.client 0026 import win32event, win32api 0027 import pythoncom 0028 0029 from testservers import TestInterp 0030 0031 freeThreaded = 1 0032 0033 def TestInterpInThread(stopEvent, interp): 0034 try: 0035 DoTestInterpInThread(interp) 0036 finally: 0037 win32event.SetEvent(stopEvent) 0038 0039 def DoTestInterpInThread(interp): 0040 try: 0041 pythoncom.CoInitialize() 0042 myThread = win32api.GetCurrentThreadId() 0043 0044 if freeThreaded: 0045 interp = pythoncom.CoGetInterfaceAndReleaseStream(interp, pythoncom.IID_IDispatch) 0046 interp = win32com.client.Dispatch(interp) 0047 0048 TestInterp(interp) 0049 interp.Exec("import win32api") 0050 print "The test thread id is %d, Python.Interpreter's thread ID is %d" % (myThread, interp.Eval("win32api.GetCurrentThreadId()")) 0051 interp = None 0052 pythoncom.CoUninitialize() 0053 except: 0054 traceback.print_exc() 0055 0056 def BeginThreadsSimpleMarshal(numThreads): 0057 """Creates multiple threads using simple (but slower) marshalling. 0058 0059 Single interpreter object, but a new stream is created per thread. 0060 0061 Returns the handles the threads will set when complete. 0062 """ 0063 interp = win32com.client.Dispatch("Python.Interpreter") 0064 ret = [] 0065 for i in range(numThreads): 0066 hEvent = win32event.CreateEvent(None, 0, 0, None) 0067 interpStream = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, interp._oleobj_) 0068 thread.start_new(TestInterpInThread, (hEvent, interpStream)) 0069 ret.append(hEvent) 0070 return ret 0071 0072 # 0073 # NOTE - this doesnt quite work - Im not even sure it should, but Greg reckons 0074 # you should be able to avoid the marshal per thread! 0075 def BeginThreadsFastMarshal(numThreads): 0076 """Creates multiple threads using fast (but complex) marshalling. 0077 0078 The marshal stream is created once, and each thread uses the same stream 0079 0080 Returns the handles the threads will set when complete. 0081 """ 0082 interp = win32com.client.Dispatch("Python.Interpreter") 0083 if freeThreaded: 0084 interp = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, interp._oleobj_) 0085 ret = [] 0086 0087 for i in range(numThreads): 0088 hEvent = win32event.CreateEvent(None, 0, 0, None) 0089 thread.start_new(TestInterpInThread, (hEvent, interp)) 0090 ret.append(hEvent) 0091 return ret 0092 0093 def test(fn): 0094 print "The main thread is %d" % (win32api.GetCurrentThreadId()) 0095 events = fn(2) 0096 numFinished = 0 0097 while 1: 0098 try: 0099 # Specifying "bWaitAll" here seems to prevent messages??? 0100 rc = win32event.MsgWaitForMultipleObjects(events, 0, 2000, win32event.QS_ALLINPUT) 0101 if rc >= win32event.WAIT_OBJECT_0 and rc < win32event.WAIT_OBJECT_0+len(events): 0102 numFinished = numFinished + 1 0103 if numFinished >= len(events): 0104 break 0105 elif rc==win32event.WAIT_OBJECT_0 + len(events): # a message 0106 # This is critical - whole apartment model demo will hang. 0107 pythoncom.PumpWaitingMessages() 0108 else: # Timeout 0109 print "Waiting for thread to stop with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()) 0110 except KeyboardInterrupt: 0111 break 0112 0113 if __name__=='__main__': 0114 test(BeginThreadsSimpleMarshal) 0115 # test(BeginThreadsFastMarshal) 0116 win32api.Sleep(500) 0117 # Doing CoUninit here stop Pythoncom.dll hanging when DLLMain shuts-down the process 0118 pythoncom.CoUninitialize() 0119 if pythoncom._GetInterfaceCount()!=0 or pythoncom._GetGatewayCount()!=0: 0120 print "Done with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()) 0121 else: 0122 print "Done." 0123
Generated by PyXR 0.9.4