如何使用 IConnectionPointContainer 和 IConnectionPoints 正确释放 COM 对象

How to free a COM Object with IConnectionPointContainer and IConnectionPoints properly

我有一个 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;
  }
}

总结评论:这应该导致正确处置 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); 将增加引用计数:

If the call is successful, the caller is responsible for releasing the connection point by calling Release when the connection point is no longer needed.

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

FinalReleaseComObject 方法:

The FinalReleaseComObject method releases the managed reference to a COM object. Calling this method is equivalent to calling the ReleaseComObject method in a loop until it returns 0 (zero).

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