4

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 ()
Nickpick
  • 6,163
  • 16
  • 65
  • 116

1 Answers1

4

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.

Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
  • It seems that it's in fact not the gui that's causing the problem, it's the com interface that is initialized by a thread and I can then not access it from a separate thread. Haven't found out yet how to best transfer the 'apartment' to the new thread – Nickpick Dec 19 '16 at 17:19
  • If you can't transfer the thing to a new thread, which seems likely, I'd create some kind of nice wrapper interface that allows you to do your communication, if the existing interface is clunky (i.e. create an adapter for the thing you're trying to use) – Wayne Werner Dec 19 '16 at 18:19
  • The problem is that the com interface is initialized with an import at the beginning of the program. I then need to set some values with a button in the gui and calculate an output with another button, so inherently both buttons would create a new threads. Haven't really found a way to create an adapter so far. – Nickpick Dec 19 '16 at 18:52
  • Your GUI should have one `window.something_comish` property (or whatever the name is). Then in your button press handlers you can do `self.something_comish.do_the_things(whatever, data, you, might, have)` – Wayne Werner Dec 19 '16 at 19:30
  • Yes but the execution of the com after the button is pressed takes a long time so I need a separate thread or it blocks the gui which isn't nice – Nickpick Dec 19 '16 at 19:35
  • No. It's initialised from the import and then continues to the gui infinite loop – Nickpick Dec 19 '16 at 19:38