Question

So, I am having this following snippet which attempts to start Microsoft Powerpoint through the win32api:

import threading
import win32com.client
import sys

class myDemo(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        try:
            myObject = win32com.client.Dispatch("Powerpoint.Application")
            print "OK"
        except:
            print "Failed to start Powerpoint!"
            sys.exit(1)            
        print "Now attempting to shutdown..."
        try:
            myObject.quit()
        except:
            print "Error"


if __name__ == "__main__":
    test = myDemo()
    test.start()

The problem is that it fails and I have no clue why. However, if I change the last line to test.run() it will launch successfully. So again why is this failing with test.start()?

Why is this happening and how should I solve it considering I need Powerpoint to run on a seperate thread asynchronously?

Thanks in advance.

EDIT: Apparently my question is somehow related to this: http://python.6.x6.nabble.com/Dispatch-error-CoInitialize-has-not-been-called-td1951088.html

However apart from the proposed proper solution no one seems to answer why exactly COM is behaving this way.

Was it helpful?

Solution

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.

OTHER TIPS

OK, so I think I found an answer but I am not yet sure why it works..

If I cut and paste this line import win32com.client from the top of the page right inside the try block where I dispatch microsoft powerpoint, the app works successfully.

However, I still can't find out why.

There are at least two more ways to solve the issue:

  1. Use run() method instead of start(), i.e. test.run()

  2. Before myObject = win32com.client.Dispatch("Powerpoint.Application") insert the following lines: import pythoncom; CoInitialize()

Notice that using run() instead of start() has been tested in other scripts and it always worked for me!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top