首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在不知道coclass的情况下发布COM接口

在不知道coclass的情况下发布COM接口
EN

Stack Overflow用户
提问于 2012-07-16 02:17:14
回答 3查看 1.6K关注 0票数 4

我有一个从本机C++ COM服务器dll使用接口的C#客户端。DLL实现了4个接口。这4个接口是由DLL中的4个coclasses实现的。但是只有一个coclass向客户端公开。接口2、3、4由接口1中的方法之一返回给客户端。

C++ COM服务器:

代码语言:javascript
复制
interface IFace1: IUnknown{
HRESULT CreateOtherInterface([in] REFIID iidFace, [out, iid_is(iidFace)] void** ppOut);
};

coclass ClassIFace1
{
    [default] interface IFace1;
};

C#客户端:

代码语言:javascript
复制
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对象?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-07-16 10:13:13

就像汉斯说的那样,CreateOtherInterface看起来很奇怪。通常,您不需要自己创建它。您需要做的就是确保客户端可以访问所有四个coclasses。然后,Activator.CreateInstance或原生CoCreateInstance将为您做正确的事情。另一种选择是公开一个单独的coclass,并让这个coclass支持所有四个接口。

但是,既然你提到只有1个coclass向客户端公开,我想可能有一些奇怪的原因,客户端使用的TLB文件看不到其他3个coclass,或者其他3个coclass没有正确注册,而是被第一个coclass以某种专有方式发现。我还假设您不能修改服务器端实现。

考虑到所有这些假设,以下是我的答案。引用计数独立地维护在4个coclasses内。因此,释放第一个coclass上的引用不会减少其他三个coclass中的引用计数。

还有一些你需要注意的事情。您正在使用Marshal.ReleaseComObject(Face1Obj)发布第一个coclass。您可以这样做,因为第一个coclass由Runtime Callable Wrapper (RCW)包装。正如马丁所说,即使你不调用Marshal.ReleaseComObject(),当垃圾收集发生时,.NET运行时也会为你做这件事。

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

如果CreateOtherInterface的实现类似于QueryInterface,它总是在返回的接口上执行AddRef,那么在使用完Marshal.Release之后,您应该在返回的接口上调用Face2Obj。Marshal.ReleaseComObject()是不够的,因为它只是释放由RCW添加的引用计数,但在本例中,您需要在IUnknown.Release上进行多个调用

票数 1
EN

Stack Overflow用户

发布于 2012-07-16 04:20:42

这是COM对象的正常行为,您只能通过它们的接口访问它们。coclass只有在创建COM对象的新实例时才是必需的,您不能直接访问coclass。所以我怀疑你的例子应该是这样的:

代码语言:javascript
复制
IFace1 face1Ctrl = new ClassIFace1();

对我来说,CreateOtherInterface()方法看起来有点奇怪,它和QueryInterface()有相同的签名,所以我假设它应该做同样的事情(我不熟悉C++):

代码语言:javascript
复制
IFace2 face2Ctrl;
face1Ctrl.CreateOtherInterface(IFace2, out face2Ctrl);

我想这应该行了,试一试吧。如果它是普通的QueryInterface方法,你应该能够得到这样的接口:

代码语言:javascript
复制
IFace2 face2Ctrl = face1Ctrl as IFace2;

COM对象是引用计数的,一旦您销毁对接口的最后一个引用,它们就会被释放。一旦垃圾回收器销毁了引用COM对象的变量,COM对象就会自动释放。这在C#中可能是一个问题,因为您必须等待垃圾收集器,并且无法确定释放的顺序。如果需要在给定时刻释放COM对象,可以使用Marshal.ReleaseComObject(),但通常只需等待垃圾回收器递减引用计数器即可。

只要您不知道COM对象的实现,就不知道是每个接口都有自己的coclass,还是一个coclass实现了多个接口。在使用COM对象时,您不应该需要此知识。查询接口可以创建一个新的coclass并返回它的接口,或者它可以返回自己并增加引用计数器。

票数 1
EN

Stack Overflow用户

发布于 2012-07-17 15:27:56

此外,在如何获取接口方面也有一些错误。以下是C#客户端的完整解决方案:

代码语言:javascript
复制
//======Create IFace1 and IFace2 interface===============
Type consoleType = Type.GetTypeFromCLSID(Face1CoCLSID);
Object Face1Obj = Activator.CreateInstance(consoleType);
IFace1 Face1Ctrl = (IFace1)Face1Obj;

Guid IFace2Guid = typeof(IFace2).GUID;
IntPtr Face2IntPtr = IntPtr.Zero;

//Face2 object's ref count will go up 1
Face1Ctrl.CreateOtherInterface(Face2Guid, out Face2IntPtr); 

//Face2 object's ref count will go up 2. One by "GetObjectForIUnknown()" 
//and one by "as", since the "as" will trigger .Net to call QueryInterface()
IFace2 Face2Ctrl = Marshal.GetObjectForIUnknown(Face2IntPtr) as IFace2; 

//=============Consume Face2Ctrl=========================

//======Destroy IFace1 and IFace2 interface===============

if (Face2Ctrl != null)
{
//Release 3 times as there were 3 RefCount obtained.

    Marshal.Release(Face2IntPtr);
    Marshal.Release(Face2IntPtr);
    Marshal.Release(Face2IntPtr);
    Face2Ctrl = null;     
}

if(Face1Obj != null)
{
//both Face1 object and Face2 object will get FinalRelease() after
//this line.
    Marshal.ReleaseComObject(Face1Obj); 
    Face1Obj = null;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11494416

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档