Using Win32Com with Multithreading

Using win32com with multithreading

If you want to use win32com in multiple threads you need to do a little bit of work more as COMObject cannot be passed to a thread directly. You need to use CoMarshalInterThreadInterfaceInStream() and CoGetInterfaceAndReleaseStream() to pass instance between threads:

import pythoncom, win32com.client, threading, time

def start():
# Initialize
pythoncom.CoInitialize()

# Get instance
xl = win32com.client.Dispatch('Excel.Application')

# Create id
xl_id = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, xl)

# Pass the id to the new thread
thread = threading.Thread(target=run_in_thread, kwargs={'xl_id': xl_id})
thread.start()

# Wait for child to finish
thread.join()

def run_in_thread(xl_id):
# Initialize
pythoncom.CoInitialize()

# Get instance from the id
xl = win32com.client.Dispatch(
pythoncom.CoGetInterfaceAndReleaseStream(xl_id, pythoncom.IID_IDispatch)
)
time.sleep(5)

if __name__ == '__main__':
start()

For more info see: https://mail.python.org/pipermail/python-win32/2008-June/007788.html

Pythoncom - Passing same COM object to multiple threads

What happens is you put back in the "activePool" a COM reference that you got from CoGetInterfaceAndReleaseStream. But this reference was created specially for this new thread and then you call CoMarshalInterThreadInterfaceInStream on this new reference.

That's what is wrong.

You must always use the original COM reference you got from the thread that created it, to be able to call CoMarshalInterThreadInterfaceInStream repeatedly.

So, to solve the problem, you must change how your apppool works, use some kind of a "in use" flag but don't touch the original COM reference.

How to launch win32 applications in separate threads in Python

I'm afraid your question likely can't be summed up in one or two sentences due to complexities in COM and threading and why they work the way they do. But for starters, here's some good information why COM behaves the way it does under threading:

http://msdn.microsoft.com/en-us/library/ms809971.aspx

Additionally, you should consider reviewing the book Python Programming on Win32. It contains useful information that sheds more light on COM threading. (Despite its age it is still useful.)

Finally, in case it wasn't clear from the reference you provided, whenever your program uses threads and COM, you must indicate in code that you're going to use COM within a thread:

import pythoncom
import win32com.client

### ... inside the thread function ...
x = win32com.client.Dispatch("someCOMobject")
win32com.CoInitialize()
# com calls here
win32com.CoUninitialize()

This type of call uses what's called single-apartment threading. It occurs when the threaded code itself instantiates COM objects.

If you find yourself instantiating a single COM object outside the threaded code (and using the instantiated object in the threaded code e.g. passing access to the COM object between threads), then this type of COM threading is called multithreaded-apartment threading:

import sys
sys.coinit_flags = 0

import pythoncom
import win32com.client

# ... outside the thread function ...
x = win32com.client.Dispatch("someCOMobject")

# ... inside the thread function ...
pythoncom.CoInitialize(pythoncom.COINIT_MULTITHREADED)
# com calls here for x
pythoncom.CoUninitialize()

Hope this helps.

How to use python and windows com in pyramid (threads)?

So i added call of CoInitialize in function:

@subscriber(NewRequest)
def new_request_subscriber(event):
import pythoncom
pythoncom.CoInitialize()

and works without exception.

Memory Leak in Threaded COM Object with Python

As it turns out this increase in Memory was in fact due to the COM object written in .NET and had nothing to do with threading. Here is a detailed description of Task Manager giving misleading information about memory usage for .NET apps. To resolve this issue I added the following to my code and I am all set. Hopefully someone else reads this response before they start tearing their hair out trying to find a memory leak in their code.

from win32process import SetProcessWorkingSetSize
from win32api import GetCurrentProcessId, OpenProcess
from win32con import PROCESS_ALL_ACCESS

import win32com.client
import threading
import pythoncom

def CreateTom():
pythoncom.CoInitialize()
tom = win32com.client.Dispatch("TOM.Document")
tom.Dataset.Load("FileName")
tom.Clear()
pythoncom.CoUninitialize()
SetProcessWorkingSetSize(handle,-1,-1) #Releases memory after every use

pid = GetCurrentProcessId()
handle = OpenProcess(PROCESS_ALL_ACCESS, True, pid)

for i in range(50):
t = threading.Thread(target = CreateTom)
t.daemon = False
t.start()


Related Topics



Leave a reply



Submit