I have a C++ out-of-process COM server that hosts a lot of C# code to support the API exposed by the C++ COM objects.
For a variety of reasons, I am considering eliminating the C++ portion of my solution. However, because of constraints outside of my control I have to retain the out-of-process COM server. Microsoft does have a canonical example of this here.
Looking at this example there is something I don't understand. Before the message loop starts, a timer is created to call GC.Collect every 5 seconds. The only mention of this that I can find indicates it's to ensure the COM objects are released in a reasonable timeframe. I'm a little confused about this...does my C++ host currently call GC.Collect automatically? I'm certainly not doing it. And yet I am creating managed objects (with COMVisible(true) as COM objects in the C++ code. Does that mean I should be calling GC.Collect every 5 seconds now? If not, why do I need to call it in this new C# out of process server. Is that to make up for the automatic process that cleans up unreferenced COM objects in a normal C++ application? (Which I assume is happening sometime during the message loop.)
Calling GC.Collect every 5 seconds seems like it could be a bad idea. Am I wrong to worry? Is there some other method by which I could achieve the same results?
I am using .NET 4.5 and Visual Studio 2012.
IMO, the easiest way to create a COM out-of-proc server in C# is to use a DLL surrogate process.
You're still limited to dual interfaces (
ComInterfaceType.InterfaceIsDual
), and you'd need to register the generated type library (and do it as part of deployment, too):C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe ManagedServer.dll /codebase /tlb
This will allow you to utilize the COM type library marshaller, because you don't have a dedicated COM proxy/stuf DLL for your C# COM objects.
Make sure to use the correct
RegAsm.exe
binary, depending on the target bit-ness of yourManagedServer.dll
assembly. The above assumes x86 code.Here is a complete working template example. It takes care of the surrogate registration:
By default, the objects will be created in MTA apartment, so the interface methods may possibly be called on any thread, you'd need to implement thread safety.
If you need an STA thread with message pump inside the surrogate process for your objects, you could do that explicitly by implementing a factory singleton and using
CoMarshalInterThreadInterfaceInStream
/CoGetInterfaceAndReleaseStream
to export objects outside the STA thread (this might be related).Another point, your COM client code should use
CLSCTX_LOCAL_SERVER
when creating this an instance ofManagedComObject
. This is not the case withActivator.CreateInstance(Type.GetTypeFromProgID("Noseratio.ManagedComObject"))
, which apparently usesCLSCTX_ALL
. This can be easily taken care of: