Is STA Message Loop Required in This Case?

2020-02-07 06:23发布

问题:

I've got some COM objects I'm creating and running on threads in a .NET application. The threads are marked as Single Threaded Apartments, and everything seems to be working. My understanding is that if these threads try to access COM objects from the main thread then those objects will automatically be marshaled and serialized for me in .NET, so even in that case things will be handled for me, all safe and neat, though maybe a bit slowly.

My question is, while things appear to be working fine, I am not pumping a message loop in the STA threads I'm creating. I'd rather avoid the message loop if I can because of the extra complications it would cause (and possible efficiency losses as well).

I've read a bunch of advice on why the message loop is necessary (mostly from the very helpful Hans Passant), and my understanding is that the message loop gives a place on a thread A where some other thread B can request that COM objects living on thread A can be marshaled and played with. If that's correct, then as long as no other threads request anything from the COM objects on thread A, is Thread A safe in not pumping a message loop? Or are there other cases where the message loop might come into play as well?

Am I playing with fire here? And is there ever a case where you ask if you're playing with fire and you're not?

回答1:

The STA contract requires pumping a message loop. But yes, it is possible to get away with not pumping. There are two major things that can go wrong:

  • Any call that's made on an interface method from another apartment, including another STA thread or a thread in the MTA will not complete. This looks like deadlock in your program, the call simply never returns. Beware that you can control your own calls quite well but you don't know what the COM component is doing. It may well start a thread itself. You can see this in the debugger with Debug + Windows + Threads. Make sure you are running the debugger in unmanaged mode and that you can account for all the threads you see. Not particularly easy btw.

  • Many apartment threaded COM components count on having a message loop taking care of their own needs. It could be something innocuous as a Timer, it won't tick when there's no loop. Or it may do marshaling internally. Use Spy++ and check if there are any hidden windows owned by your new STA thread, sure sign of trouble if you see one. The diagnosis is the component just misbehaving. Not raising events is a common mishap.

Nothing really to nail to a wall when you don't know enough about the internals of the server. Be sure to test the heck out of it.



回答2:

COM calls can be marshaled without a message loop.

When your STA thread is waiting for something it will in most cases automatically handle any pending COM calls.

The documentation for Thread.Join says Blocks the calling thread [...] while continuing to perform standard COM and SendMessage pumping.

The same happens for many other functions called on your behalf (CoWaitForMultipleHandles etc), e.g. when your thread is waiting for IO.