none
How to run a STA COM component in background? RRS feed

  • Question

  • I am writing a VSTO application that makes calls to a third party STA COM component.  I moved a number of time consuming operations to a BackgroundWorker thread to free up the UI.  These operations include calls to the COM component.  My understanding is that although the BackgroundWorker is MTA and the COM component is STA I can still make calls to the COM component and it certainly seems that way so far.

    The only problem is that a couple of the COM calls involve time consuming operations themselves and the UI stops responding while they are being executed.  Is this a problem with the use of the BackgroundWorker and is there a way to ensure that these operations by the COM component don't affect the UI?

    Thanks, Tad
    Monday, November 17, 2008 5:08 AM

Answers

  • You have to start a new thread, set the threading model to STA, drop the priority, and create the object on that thread. It works fine from thereon in. I've done it.

    The way it works is that STA objects created on MTA thread get created on the "primary" STA thread (the UI thread for all practical purposes). STA objects created on STA threads belong to the thread they were created on.

    Under rare circumstances, in native mode, you would need to pump messages if you were waiting for asynchronous STA COM operations to complete; and that might get nasty under .net. But it's not a common occurence; and some, if not all .net wait primitives seem to do the right thing anyway.

    A forseeable complication, though: even if you create your object on a background thread, you can't really pass it any references to objects from the foreground thread, because calls to methods on those objects will be marshalled onto the foreground thread as well. In small doses, that's survivable, as long as the calls to the main-thread objects are not very chatty. I didn't get a clear answer on whether .net automatically marshals COM pointers between trheads. I'm guessing it does. Anyway. the whole scheme really works only if your background operation is totally (or almost totally) standalone with respect to COM objects created on the UI thread.

    • Marked as answer by Tadwick Wednesday, November 19, 2008 7:52 PM
    Wednesday, November 19, 2008 9:56 AM
  • Starting an STA thread but not pumping a message loop is a big no-no in COM threading.  You'll have to create the object on that thread and avoid using any of its interfaces on another thread.  And hope it doesn't use a message loop itself, they often do.  At that point, there really is no reason left to make it STA since nothing is getting marshaled anymore.  Maybe it works.  Trouble is easy to diagnose, it will just deadlock.
    Hans Passant.
    • Marked as answer by Tadwick Wednesday, November 19, 2008 7:52 PM
    Wednesday, November 19, 2008 10:07 AM
    Moderator

All replies

  • There's no problem with that, as long as you create a fresh thread, and set the threading model of the background thread to STA:

      Thread.SetAppartmentState(AppartmentState.STA).

    before you instantiate the COM object.

    There are things you can't do:

    - You can't use the UI of the COM object.
    - You can't pass pointers to the the COM object between threads directly. And you don't want to do it indirectly. Marshalling a pointer between STA threads is pointless, because the time-consuming operations will still operate on the
    original thread.


    Monday, November 17, 2008 5:22 AM
  • I'm using the specific BackgroundWorker class (from the System.ComponentModel namespace), which I believe overrides any attempt to specify the appartment state.  The COM component doesn't have a UI so that is not of concern.

    To rephrase the question, is the problem that I am using a BackgroundWorker class and, if so, should I fall back to the Thread class, which will let me specify the apartment state?
    Monday, November 17, 2008 5:36 AM
  • BackgroundWorker threads come from a thread pool, and are irrevocably MTA threads.

    So, consider what happens when you create an STA COM object on an MTA thread. The default COM behaviour is actually to create the STA COM object on the primary STA thread (ie.  the UI thread) in the application. And then any subsequent call to the COM object actually gets marshalled onto the main STA thread. So, even though you're making calls from the BackgroundWorker thread, all code in the COM object is being run on the main thread. Probably not what you need.


    Previous postings here, on the subject of MTA-ness seem to suggest that MTA threading is unavoidable on a BackgroundWork thread.

    Short answer: BackgroundWorker thread won't do it for you.

    I'm curious if anyone else can answer the question of whether simply using a .net COM interrop wrapper on a new thread automatically marshal the internal COM pointers between threads. I'm guessing -- given the consequences of NOT doing that to .NET safety -- that it must.


    Monday, November 17, 2008 7:00 AM
  • Yes, as long as you created the COM component on the main UI thread, and that thread is pumping a message loop like it should, you can safely call methods of that component on a BGW or any other flavor of thread.  COM's STA threading model ensures that this will work as expected, it automatically marshals the method call from the background thread to the UI thread.

    But, of course, that makes it pointless to use the background thread in the first place.  You cannot whack code into multi-threading mode if the original programmer didn't write the locks and what-not to ensure that his code runs correctly.  Which very few COM programmers ever did.  They announce they didn't by setting ThreadingModel = Apartment in the registry.  STA.

    Hans Passant.
    Monday, November 17, 2008 12:52 PM
    Moderator
  • Hans and Robin,

    Thank you for your responses.  So if I understand you both correctly the MTA BackgroundWorker approach is inappropriate (at least it doesn't provide any advantages for COM calls made on a BackgroundWorker thread).  Can I use the Thread class instead with apartment state set to STA and IsBackground set to true - or will COM still marshall the method calls to the primary UI thread?

    Thanks, Tad

    Monday, November 17, 2008 4:13 PM
  • You have to start a new thread, set the threading model to STA, drop the priority, and create the object on that thread. It works fine from thereon in. I've done it.

    The way it works is that STA objects created on MTA thread get created on the "primary" STA thread (the UI thread for all practical purposes). STA objects created on STA threads belong to the thread they were created on.

    Under rare circumstances, in native mode, you would need to pump messages if you were waiting for asynchronous STA COM operations to complete; and that might get nasty under .net. But it's not a common occurence; and some, if not all .net wait primitives seem to do the right thing anyway.

    A forseeable complication, though: even if you create your object on a background thread, you can't really pass it any references to objects from the foreground thread, because calls to methods on those objects will be marshalled onto the foreground thread as well. In small doses, that's survivable, as long as the calls to the main-thread objects are not very chatty. I didn't get a clear answer on whether .net automatically marshals COM pointers between trheads. I'm guessing it does. Anyway. the whole scheme really works only if your background operation is totally (or almost totally) standalone with respect to COM objects created on the UI thread.

    • Marked as answer by Tadwick Wednesday, November 19, 2008 7:52 PM
    Wednesday, November 19, 2008 9:56 AM
  • Starting an STA thread but not pumping a message loop is a big no-no in COM threading.  You'll have to create the object on that thread and avoid using any of its interfaces on another thread.  And hope it doesn't use a message loop itself, they often do.  At that point, there really is no reason left to make it STA since nothing is getting marshaled anymore.  Maybe it works.  Trouble is easy to diagnose, it will just deadlock.
    Hans Passant.
    • Marked as answer by Tadwick Wednesday, November 19, 2008 7:52 PM
    Wednesday, November 19, 2008 10:07 AM
    Moderator
  • Robin and Hans,

    Thank you again for your informative responses.  I am doing everything I can to keep all the COM objects created on the background thread.  I am also working with the COM component developer to see if there are marshalling issues that can be addressed.

    Tad
    Wednesday, November 19, 2008 7:52 PM