2

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?

user12861
  • 2,358
  • 4
  • 23
  • 41
  • Do you have events? Do you have control over the event handlers to make sure their re-entry calls won't cause infinite recursion? Posting message is usually used to break this loop, if you have a message pump. – Sheng Jiang 蒋晟 May 18 '12 at 15:24
  • I guess I'm not aware of the issues associated with this. What would cause an event to be raised in a thread that I created and no one else is looking at? The COM objects I'm using? As far as I know they don't raise any events... – user12861 May 18 '12 at 17:39
  • You implement outgoing event interfaces like IConnectionPointContainer if your COM object's design includes notifying the outside world what is going on. – Sheng Jiang 蒋晟 May 18 '12 at 18:18

2 Answers2

4

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Insightful answer as alway. Much appreciated. – user12861 May 22 '12 at 17:29
  • I guess we had a similar problem with the `CDO.Message` COM object. It never returned from `Send()` when the user wildly moved MDI windows around. Probably because it relied on the message queue as said and couldn't post a message into it since it the queue was full from all the window movement, and the implementation forgot to check that `PostMessage` failed. – Ray Jun 28 '16 at 12:55
1

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.

adrianm
  • 14,468
  • 5
  • 55
  • 102