【问题标题】:How to free a COM Object with IConnectionPointContainer and IConnectionPoints properly如何使用 IConnectionPointContainer 和 IConnectionPoints 正确释放 COM 对象
【发布时间】:2015-05-29 05:43:44
【问题描述】:

我有一个 COM 对象:

_myObject = (IMyInterface)Activator.CreateInstance(...);

我可以得到一个 IConnectionPointContainer:

_cpc = (IConnectionPointContainer)_myObject;

并从中获取我的 IConnectionPoint:

IConnectionPoint _cp;
_cpc.FindConnectionPoint(typeof(IMyCPInterface).GUID, out _cp);
_cp.Advise(this, out _cookie);

我现在不确定释放所有资源的正确方法。 如何正确释放所有创建的资源?

目前我按照下面的方式进行操作,但我不确定我是否正确发布了所有内容:

protected virtual void Dispose(bool pDisposing)
{
  if(!_isDisposed)
  {
    if(pDisposing)
    {
      //dispose managed
    }

    if(_cookie > 0 && _cp != null)
    {
      _cp.Unadvise(_cookie);
      _cp = null;            //<-- ReleaseComObject(_cp); ?
      _cookie = 0;
    }

    //I don't release _cpc, because it is the same object as _myObject?

    if(_myObject != null)
    {
      Marshal.ReleaseComObject(_myObject);
      _myObject = null;
    }

    _isDisposed = true;
  }
}

【问题讨论】:

  • 一般情况下:释放引用计数增加的次数。即:查询接口时(通过转换为:((IConnectionPointContainer)_myObject),您应该阅读文档以确定引用计数是否增加(通常是)。Marshal.FinalReleaseComObject 可能会有所帮助:msdn.microsoft.com/en-us/library/…跨度>
  • 根据:msdn.microsoft.com/en-us/library/windows/desktop/…,必须释放_cp。 _cpc 也必须被释放,当然 _myObject 也必须被释放,由于 IMyInterface 演员,可能会释放两次。
  • 您可以随时测试它,当您完成对象时,引用计数必须为0。

标签: c# com unmanaged


【解决方案1】:

总结 cmets:这应该会导致 COM 对象的正确处理:

protected virtual void Dispose(bool pDisposing)
{
    if(!_isDisposed)
    {
        if(pDisposing) //dispose managed
        {
            if(_cookie > 0 && _cp != null)
            {
                _cp.Unadvise(_cookie);
                _cookie = 0;
            }
            if (_cp != null)
            {
                Marshal.ReleaseComObject(_cp);
                _cp = null;    
            }
            if(_cpc != null)
            {
                //needed due to the implicit queryinterface on cast 
                //_cpc = (IConnectionPointContainer)_myObject;
                Marshal.ReleaseComObject(_cpc);
                _cpc = null;
            }
            if(_myObject != null)
            {
                //you could test if Marshal.ReleaseComObject(_myObject) == 0
                //If it's not, the IMyInterface cast is probably increasing the refcount.
                //but use FinalRelease just to be safe.
                Marshal.FinalReleaseComObject(_myObject);
                _myObject = null;
            }
        }
        _isDisposed = true;
     }
 }

_cpc.FindConnectionPoint(typeof(IMyCPInterface).GUID, out _cp); 将增加引用计数:

如果调用成功,则调用者负责在不再需要连接点时通过调用Release来释放连接点。

https://msdn.microsoft.com/en-us/library/windows/desktop/ms692476%28v=vs.85%29.aspx

FinalReleaseComObject 方法:

FinalReleaseComObject 方法释放对 COM 对象的托管引用。调用该方法相当于循环调用ReleaseComObject方法,直到返回0(零)。

https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.finalreleasecomobject%28v=vs.100%29.aspx

补充说明:

虽然查询对象上的接口可能会导致返回相同的对象,有时甚至是相同的指针值,但对ReleaseComObject 的调用仍然是合法的。这是因为内部引用计数如果达到 0 会触发对象的清理。

有关托管 RCW 和 COM 的更多信息:

https://msdn.microsoft.com/en-us/library/8bwh56xe%28v=vs.110%29.aspx

【讨论】:

  • 这些都不是必需的。 ReleaseComObject 的使用应该在您不能等待垃圾收集完成 RCW 的情况下使用,FinalReleaseComObject 应该在以下情况下使用您确定要从底层 COM 对象中分离每个 RCW。
  • @PauloMadeira:好点。我会放宽这个词。虽然我认为释放 COM 对象是一个好习惯,尤其是当它们经常被实例化时。
  • 这取决于释放 COM 对象的对象(从托管的角度)是否是它的唯一所有者。通常,实例化的对象经常属于这种情况,但不一定。例如。如果您正在迭代 Excel 工作表或单元格,如果您不确定托管代码是否会保留对其中之一的引用,则不应使用 *ReleaseComObject,而必须依赖(可能是强制的、定期的或按需的) ) 垃圾回收。
  • 当我关闭应用程序(由终结器调用Dispose(false);)时,它会在 Unadvise 崩溃并显示 InvalidComObjectException。在这种情况下,CRW(或 CCW?)比我的对象更早发布,对吗?所以不建议应该移到if(pDisposing) 块?我的意思是我试过了,它可以工作,但我不确定是否应该这样做......
  • 没错,托管对象的处置应该在if (disposing)块内,而RCW是托管对象。当它被 GC 时(因此你的对象也是如此,因为它指向 RCW),没有办法保证它在你的对象之前或之后完成。
猜你喜欢
  • 2023-03-25
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
  • 2015-09-25
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多