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

本文关键字:内存 所有权 对象 返回 C#COM 方法 | 更新日期: 2023-09-27 17:52:42

我在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#中不是这样吗?

我没有在ColorRamps.getColorRamp((.内部调用Marshal.AddRef((

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

发生这种情况的最可能原因是Release()是指针,而不是.NET在返回之前忘记了AddRef()(提示:.NET肯定没有忘记这一点(。

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

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

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

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

值得一看吗?