DCSIMG
.NET, COM and Apartments Tips - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

.NET, COM and Apartments Tips

We all know that when we use COM interop with .NET we get some RCW (Runtime Callable Wrapper) CLR object representing and controlling access to the underlying COM object. When we call the C# new operator, under the hood the RCW calls the CoCreateInstance API to create the actual COM object, etc. What may not be apparent, is that there may exist another proxy to the COM object, even if the COM server is a DLL (in-process server).

The reason this may happen is related to COM apartments. an Apartment is a logical container of threads and COM objects that share the same concurrency requirements. COM classes in a DLL indicate their requirement using the "ThreadingModel" value in the registry. The possible values include "Single", "Apartment", "Free", "Both" and "Neutral". I will discuss Apartment, Free and Both as they are the most frequent. If anyone is interested, I can go into more detail.

The setting of "Apartment" means "Single Threaded Apartment (STA)", which means exactly that. The COM object wants to be created in an Apartment in which a single thread lives. This means this is the only thread that can access the object. Any other thread gets a proxy to the object that does some marshaling of calls by sending a custom Windows message to a hidden window that is created by COM for each STA.

How can all this happen? How does a thread indicate in which apartment it wants to live? The answer is the Conitialize(Ex) COM API, which all threads that want to do COM stuff must call. in .NET, we see the [STAThread] attribute on the Main method, which means the main thread enters an STA of its own. By the way, all other threads must explicitly call Thread.SetApartmentState() to indicate the where they want to live (the default being MTA) BEFORE calling Thread.Start().

Why does the wizard set the main thread to an STA? Because of  ActiveX controls, which are COM components that have a UI (usually) and always must be synchronized with the UI thread - this is how Windows like it. All ActiveX controls are ThreadingModel=Apartment.

So, if a Thread is created in managed code and creates an ActiveX control or another COM class marked as ThreadingModel="Apartment" (meaning it wants to live in an STA), COM creates a thread, and puts it in an Apartment of its own, creates the object in that Apartment and returns a proxy to the client (the RCW). So, the .NET code calls a proxy (RCW) which calls another proxy (COM), which calls the actual object!

How can we avoid that? If we know the COM class is marked "Apartment" we can call Thread.SetApartmentState with the ApartmentState.STA value to avoid the proxy.

COM classes marked as "Free" live in the only MTA in the process, indicating they require no synchronization, BUT they will get a proxy if created from an STA (e.g. the Main thread in WinForms, WPF or Console app)... why? Because of potential callbacks (or events) back to the STA that must be used with a proxy. We can avoid that by calling Thread.SetApartmentState with ApartmentState.MTA (unless it's a UI thread, and then we're in trouble again).

The (almost) best COM class is marked with "Both" meaning it will be created in the Apartment of the client thread, never producing a proxy. A "Free" class can usually be changed to "Both" with no ill effect. I say "usually", because if that COM class creates worker threads that enter the MTA they may incur a proxy talking back to the object, but this is rather unusual.

The danger with "Both" is that is if the COM object is passed to another Apartment, a proxy will be created for that Apartment, regardless of the object's best efforts to indicate it requires no synchronization.

The best COM classes are marked with "Both" and aggregate the Free Threaded Marshaler (FTM), indicating a proxy will NEVER be created for those. How can you tell if a COM class that has ThreadingModel="Both" uses the FTM? There is no sure way, but a 99.99% way is to open OleView.Exe, navigate to the class and open the node in the tree. If the IMarshal interface is implemented, then 99.99% it's the FTM's implementation.

Summary: If you have Both+FTM class you never get a proxy - the best option.

If you have a "Free" class and you're using each instance through one thread (not passing the same instance between threads), try changing to "Both". Most likely you'll get all the benefits of no COM proxy.

If the class is marked "Apartment" try to use it from an STA ([STAThread] or SetApartmentState(ApartmentState.STA)). If you can't, you can turn it into "Both" if you supply thread synchronization, or promise not concurrently access an instance.

.NET has no notion of Apartments, so pure .NET code conceptually live in a giant MTA - synchronization is the object's responsibility. in the COM world, synchronization was not always possible (e.g. VB6), multithreading was still a new concept, etc, so Apartments were invented.

Enjoy it the COM way...

 

Comments List

# re: .NET, COM and Apartments Tips

Published at Wednesday, November 26, 2008 9:19 AM by Apartments Rental  

Very Informative post on Apartments Tips.

Leave a Comment

(required) 
(
required
)
 
(optional)
(required) 

Enter the numbers above: