c# – 在不知道coclass的情况下发布COM接口

我有一个C#客户端,它使用本机C COM服务器DLL的接口.该DLL实现了4个接口.这4个接口由DLL中的4个coclasses实现.但是只有1个coclass暴露给客户端.接口2,3,4通过接口1中的一种方法返回给客户端.

C COM服务器:

interface IFace1: IUnknown{
HRESULT CreateOtherInterface([in] REFIID iidFace, [out, iid_is(iidFace)] void** ppOut);
};

coclass ClassIFace1
{
    [default] interface IFace1;
};

C#客户端:

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);
}

由于IFace2,IFace3和IFace4不与IFace1共享相同的coclass,我怀疑Marshal.ReleaseComObject(Face1Obj)行只会破坏ClassIFace1对象而不会破坏ClassIFace2,ClassIFace3,ClassIFace4对象并导致内存泄漏.有什么方法可以解决这个问题吗?或者Marshal.ReleaseComObject(Face1Obj)实际上也会破坏其他COM对象?

最佳答案 就像Hans说的那样,CreateOtherInterface看起来很奇怪.通常,您不需要自己创建它.您需要做的就是确保客户端可以访问所有四个coclass.然后,Activator.CreateInstance或本机CoCreateInstance将为您做正确的事情.另一种选择是暴露一个单一的coclass,并拥有支持所有四个接口的单个​​coclass.

但是,由于你提到只有1个coclass暴露给客户端,我想象有一些奇怪的原因,客户端使用的TLB文件没有看到其他3个coclasses或其他3个coclass没有正确注册但是由一些专有方式的第一个coclass.我也假设您无法修改服务器端实现.

鉴于所有这些假设,这是我的答案.引用计数独立地保留在4个coclass中.因此,释放第一个coclass上的引用不会减少其他三个coclass中的引用计数.

还有一些事情你需要注意.您正在使用Marshal.ReleaseComObject(Face1Obj)来释放第一个coclass.你可以这样做,因为第一个coclass由Runtime Callable Wrapper(RCW)包装.就像Martin所说,即使你不调用Marshal.ReleaseComObject(),.NET运行时也会在发生garbabge收集时为你做.

但是,Face2Ctrl的获得方式不同.它不是由RCW包裹的.您将返回的指针直接视为结构.这对我来说听起来不对,因为您可能在内存对齐和数据编组方面存在问题.你想要做的可能是调用Marshal.GetObjectForIUnknown,它会为你返回一个RCW.获得RCW后,您可以调用Marshal.ReleaseComObject()以及时释放您的RCW.

如果CreateOtherInterface的实现类似于QueryInterface,它总是在返回的接口上添加AddRef,那么一旦完成Face2Obj,就应该在返回的接口上调用Marshal.Release. Marshal.ReleaseComObject()是不够的,因为它只释放了RCW添加的引用计数,但在这种情况下,你需要在IUnknown上调用一次更多.

点赞