I have a C# client that consumes interfaces from a native C++ COM server dll. The DLL implements 4 interfaces. These 4 interfaces are implemented by 4 coclasses in the DLL. But only 1 coclass is exposed to the client. Interfaces 2,3,4 are returned to the client by one of the methods in interfaces 1.
C++ COM server:
interface IFace1: IUnknown{
HRESULT CreateOtherInterface([in] REFIID iidFace, [out, iid_is(iidFace)] void** ppOut);
};
coclass ClassIFace1
{
[default] interface IFace1;
};
C# Client:
ClassIFace1 Face1Obj = new ClassIFace1();
IFace1 Face1Ctrl = (IFace1)Face1Obj;
IFace2 Face2Ctrl = null;
IntPtr Face2IntPtr = new IntPtr();
Face1Ctrl.CreateOtherInterface(Face2Guid, out Face2IntPtr);
Face2Ctrl = (IFace2)Mashal.PtrToStructure(Face2IntPtr);
//Consume Face2Ctrl
if(Face1Obj != null)
{
Marshal.ReleaseComObject(Face1Obj);
}
As IFace2, IFace3 and IFace4 does not share the same coclass as IFace1, I suspect the Marshal.ReleaseComObject(Face1Obj) line will only destruct ClassIFace1 object but not ClassIFace2, ClassIFace3, ClassIFace4 objects and results in memory leaks. Is there any way to solve this? Or the Marshal.ReleaseComObject(Face1Obj) actually destroy other COM objects as well?
Like what Hans said,
CreateOtherInterfacelooks very odd. Normally, you don’t need to create it by yourself. All you need to do is to make sure client can access all four coclasses. Then, theActivator.CreateInstanceor the nativeCoCreateInstancewill do the right thing for you. Another choice is to expose one single coclass and have that single coclass supporting all four interfaces.However, since you mentioned that only 1 coclass is exposed to client, I am imagining there are some strange reasons that the TLB file consumed by the client doesn’t see the other 3 coclasses or the other 3 coclasses were not registered properly but discovered by the first coclass in some proprietary ways. I am also assuming that you cannot modify server side implementation.
Given all these assumption, here is my answer. The reference counts are maintained within the 4 coclasses independently. So, releasing the reference on the first coclass won’t decrement the reference counts in the other three coclasses.
There are some more things you need to pay attention. You are using
Marshal.ReleaseComObject(Face1Obj)to release the first coclass. You can do that because the first coclass was wrapped by a Runtime Callable Wrapper (RCW). Like what Martin said, even if you don’t call theMarshal.ReleaseComObject(), .NET runtime will do it for you when garbabge collection occurs.However, Face2Ctrl is obtained differently. It’s not wrapped by a RCW. You are treating the returned pointer as a structure directly. This doesn’t sound right to me because you may have issues on memory alignment and data marshalling. What you want to do might be calling
Marshal.GetObjectForIUnknownwhich will return a RCW for you. Once you get your RCW, you can callMarshal.ReleaseComObject()to release your RCW in timely manner.If the implementation of
CreateOtherInterfaceis likeQueryInterface, which alwaysAddRefon the returned interface, you should callMarshal.Releaseon the returned interface once you are done with Face2Obj.Marshal.ReleaseComObject()is not enough because it just releasees the reference count added by the RCW but in this case, you need more one call onIUnknown.Release