从C#COM方法和内存所有权返回对象

时间:2015-06-02 08:42:19

标签: c# c++ .net com

我在C#中实现了一个COM组件:

[ComVisible(true)]
[System.Runtime.InteropServices.Guid("E052BB1C-7ADC-47F4-99E1-9407E2FA0AA2")]
public interface IColorRamps
{
    IColorRamp getColorRamp();
}

[ComVisible(true)]
[Guid("EE47F2F2-0AD9-437C-8815-D570EACF2C07")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("ColorRamps.ColorRamps")]
public class ColorRamps : IColorRamps
{
    public IColorRamp getColorRamp() { ... } 
}

我是用C ++调用的:

IColorRampPtr colorRamp;
{
    ColorRamps::IColorRampsPtr colorRamps(ColorRamps::CLSID_ColorRamps);
    HRESULT hr = colorRamps->getColorRamp(&colorRamp);
    colorRamp.AddRef(); // Should I do this??
 }

起初我没有AddRef()调用,但事情似乎有效,除了我在" R6025(纯虚函数调用)运行时错误"上发生了奇怪的崩溃。多次运行此代码后。

自动生成的.tlh文件中的签名是:

virtual HRESULT __stdcall getColorRamp(/*[out,retval]*/ struct IColorRamp * * pRetVal ) = 0;

在C ++中调用这样的函数时,我习惯于执行AddRef()本身的函数并将内存所有权传递给调用者。在C#COM中不是这种情况吗?

我没有在ColorRamps.getColorRamp()中调用Marshal.AddRef()。

2 个答案:

答案 0 :(得分:3)

这很可能是因为你Release()指针而不是因为.NET在返回之前忘了AddRef()(提示:.NET肯定没有忘记这一点)。

IColorRampPtr本身就是一个智能指针,你必须使用它,因为你在VC ++中#imported .NET生成的类型库。因此,您永远不应该在智能指针上调用Release(),因为它会在超出范围时被释放(或者,如果在类成员中使用,则在对象被销毁时)。

如果你想要一个原始指针,你必须在其上调用Release(),使用原始接口指针(例如IColorRamp*)或Detach() the smart pointer。通常,当范围变得不确定时,您需要一个原始接口指针。如果范围定义良好并且您可以使用智能指针,请将其保留为智能指针。

答案 1 :(得分:2)

来自MSDN(https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.addref%28v=vs.80%29.aspx):

  

公共语言运行库管理COM的引用计数   对象,使您无需直接使用此方法。在   在极少数情况下,例如测试自定义封送程序,您可能会发现它   必须手动操纵对象的生命周期。打电话后   AddRef,您必须使用这样的方法减少引用计数   作为Marshal.Release。不要依赖AddRef的返回值   有时可能会不稳定。

值得一看?