Python thread: The application called an interface that was marshalled for a different thread

3.6k views Asked by At

The following code works without errors:

 self.sim.create_pnl_results(gui_values, dfs)

This code gives me an error:

thread = Thread(target=self.sim.create_pnl_results, args=(gui_values, dfs))
thread.start()

in _ApplyTypes_ self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
com_error: (-2147417842, 'The application called an interface that was marshalled for a different thread.', None, None)

It seems that the application calls a com object somewhere further down the line and because I would like to put a gui (QT) on top I need to make it a separate thread. How can I avoid the above error message? I have read that it is connected to a windows problem. Any suggestions how to resolve it in python would be appreciated. I have no control on the com objects that are called from the application and I don't see why it would make a difference if I call them from the main thread of a new thread.

additional information about how the com object is called:

def process_ts(*args):
    ts_id, i, dfn , ts_queue = args
    pythoncom.CoInitialize()
    ts = win32com.client.Dispatch(pythoncom.CoGetInterfaceAndReleaseStream(ts_id, pythoncom.IID_IDispatch))
    ts_queue.put( tuple([i , ACRF._com_to_ts(ts, i, dfn)]) )
    pythoncom.CoUninitialize ()
1

There are 1 answers

6
Wayne Werner On BEST ANSWER

Many (if not most) GUI applications run into problems if you try to update/redraw the UI from another thread. Just imagine what would happen if you had one thread trying to write "blue" 100x to a file, and another thread trying to write "red" 100x. You'll see something like:

redblueredblruede...

(granted with Python you have the GIL so you might not get precisely that same effect, but using multiprocessing you could probably make it happen).

The exact same thing would happen to your graphical application if one thread is trying to turn a region blue, while the other is trying to turn it red. Because that's undesirable, there are guards put in place to stop it from happening by throwing exceptions like the one you encountered.

What you have happening here is something like this:

----(UI Thread)-,----------------->
                 \
                  `---(new thread)-----(affect the UI)-X kaboom!

What you want to have happen is something like this:

----(UI Thread)-,--------------------------------------,---(affect the UI)-->
                 \                                    /
                  `---(new thread)-----(pass result)-`

The exact mechanics of it will change from framework to framework. In .NET you've got an if (thing.InvokeRequired){ thing.BeginInvoke(data); }

On Windows, COM objects are protected the same sort of way. If you create an COM object on a thread it doesn't like you trying to access it on a different thread. So if you're creating it on a different thread and you still need to interact with it you're going to have to communicate across your threads.

If your code isn't blocking, or it doesn't take very long to run (i.e. < 250ms) then running the calls on the main thread should be just fine. Typically UI frameworks allow you to register a callback to be executed after N amount of time, and then it will just be executed on the main thread whenever it can be.