views:

456

answers:

2

Recently, I encountered this situation where I wanted to display a form on another thread (not the main/UI thread). I used a threadpool thread. The form hosted a RCW (for a COM component). Instantiating the form gave me an exception that the thread must be a STA. I tried to set the apartment state as STA. But, that didn't work either. I finally ended up creating a thread explicitly and that worked(I used ShowDialog and needn't create a meesage pump).

EDIT:

  • I know that threadpool threads are MTA. But, why can't it be set to STA? Just curious about this.
  • Another question that just popped in my head: Why don't we require a
    message pump when Showdialog() (to
    display a form) is used
+5  A: 

You shouldn't rely on specific behavior for thread pool threads. In general, a thread in the threadpool should be able to be replaced at any time by the CLR, without your knowledge. Thread pool threads are meant to be used with simple tasks, preferably short lived ones.

If you want to have fine grained control over thread settings, you should create a dedicated thread. Setting the apartment state is a perfect example of this.


In addition to the above, theoretical reasons, there's a practical problem with what you are attempting. Hosting a form on a second thread doesn't work (without a lot of extra work in place). Forms must be run on the same thread as the message pump - otherwise, they won't receive any windows messages, and will not update properly.

You can create a form on a separate thread if you implement a full message pump for that thread, but it's usually a better idea to just put your work items onto background threads, and use asyncrhonous programming techniques to keep your since UI thread responsive.

Reed Copsey
Thanks for the answer."A thread in the threadpool should be able to be replaced at any time by the CLR, without your knowledge." Didn't know this. What does it exactly mean?
P.K
The threadpool creates and destroys threads as your program runs. There aren't (necessarily) just a fixed number of threads in the threadpool. .NET 4's threadpool is even more flexible in this regard. It has all sorts of new tools for work stealing, etc, that cause it to spawn and destroy threads during the runtime of your app.
Reed Copsey
oh..yes I am totally aware about the message pump. So, creating a form via threadpool thread or a new thread will require a message pump to be attached to that thread. But, I use ShowDialog() to display the form and that doesn't require message pump as it is a blocking call.Actually, I was just curious about why can't we change the apartment state of a ThreadPool thread to be STA.Thanks.
P.K
ShowDialog still requires a message pump. If you do this without one, you get some odd behavior, like no redrawing (sometimes) if other windows move over the top... it works, in general, since it takes the active window and assigns it as the owner, but it's not 100% reliable, either.
Reed Copsey
Thanks for that. I didn't know that ShowDialog can create such ugly problems in such situations. Can you point me to a link/reference which says that ShowDialog creates such problems?
P.K
+1  A: 

The apartment is selected by a call to CoInitializeEx(). A thread in the thread pool has already made that call, changing the apartment after that call is not possible.

That a thread pool would choose MTA makes sense, it is after all intended as a worker thread and shouldn't be blocked by method calls that need to be marshaled. Selecting a single-threaded apartment has the additional requirement of pumping a message loop. Something you'd never expect a threadpool thread to do.

The message loop is necessary because that's the vehicle that COM uses to marshal a call made on another thread. That call has to be "injected" in the STA thread, that's only possible if the thread is in a known quiescent state. If it isn't, such a call would cause major re-entrancy problems. Which it sometimes does even if the thread is pumping the loop.

You didn't need to pump a message loop yourself with Application.Run() because ShowDialog() starts its own message loop. That's how it gains modality. That nested loop exits as soon as the dialog closes.

Hans Passant
Thanks for the answer. Can you point to a link, which says that ShowDialog() starts its own message loop.
P.K
http://www.red-gate.com/products/reflector. Recommended.
Hans Passant
Thanks. Application.RunDialog(this) does the trick.
P.K