In .Net, particularly when building Web Service provider, the prefer apartment type is MTA. Under this condition if you have to use functionality provided by a legacy COM in-proc server that has ThreadingModel=Apartment, the performance of the solution involving mixed model is severely impaired.
The problem is fully documented in MSDN under the Mixed Model Development section and interested reader should consult that article.
The way to overcome this is to construct a multi-STA server with each STA hosting just one STA com object. In this way each client living in a MTA can call the STA com object independently from other invocation.
This kind of construction requires some administrative work and hence not as simple as one may think.
However, for most COM in-proc server the way around this problem is a lot easier and the technique is to employ COM aggregation to host the STA COM component that you need to use by MTA client.
A step by steps guide to aggregate another COM component using ATL can be found here.
The construction recipe to use COM aggregation to build a multiple STA server is given below:
1) Create a standard COM local (executable) server that contains a COM component with an interface of your choosing. This interface needs not contain any useful method and is not used at all. If you are developing this with Delphi, beware of the problem.
I recommend the use of ATL as it is a nice and skinny framework.
2) Then use the COM development framework to aggregate the legacy STA COM in-proc server. It is often extremely difficult to tell if a COM component supports aggregation or not. Just build it and test it is a good way to determine that. Aggregation is a construction technique and hence does not contain any detectable COM registration information. Incidentally all managed COM component automatically support COM-style aggregation and this can be used to construct .Net COM local server.
3) Make sure when the COM local server contains only STA by calling CoInitialize() or CoInitializeEx( 0, COINIT_APARTMENTTHREADED ).
4) When COM server registers class object, make sure it uses the REGCLS_SINGLEUSE flag forcing this server support only one COM object, thus one STA in-proc server by virtue of aggregation.
This technique has one great advantage and that is you can leave the source code unchanged except in the server instantiation code. Instead of creating the in-proc STA server, you are creating the com object belonging to the aggregater server.
Because you are interested only in the interface belonging to the aggregated component, you can immediately cast the result into the target interface.
This technique is not without cost and they are:
* It trades off serialising calls to methods in STA components from MTA with context switching and data marshalling costs. Typically the STA component exposes a fine-grained object model and any navigation will incur these costs. Hence one should minimise the use of dotted expression.
* It uses more processes than otherwise required.
* Involvement of COM local server could result in a situation where the server does not terminate.
* Memory consumption can remain relatively high if the client is a .Net solution. This is due to the involvement of two object life-cycle management schemes - the .Net RCW relying on GC to reclaim resources and the COM which relies on reference counting. In this situation, particularly if the dotted expression is used to navigate down the object model, this kind of expression generates a lot internal RCW's and each level of the dotted expression corresponds to a COM object.
These internal RCW's will only be disposed when the GC collects them. If they are not disposed, the corresponding COM object is not removed. This situation increases memory demand above that if both client and server are unmanaged code. The way around this is to avoid using dotted expression.
A site devoted to discussing techniques that promote quality and ethical practices in software development.
- ► 2013 (13)
- ► 2012 (23)
- ► 2011 (23)
- ► 2010 (60)
- ► 2009 (60)
- ► 2008 (25)
- ▼ April (6)