-1

I am creating a C# wrapper over the Acrobat SDK (yes, I know there are utilities out there already). I'm importing an Adobe type library that was created in C/C++. Perhaps this is obvious but I'm assuming this is unmanaged code. Are all COM objects in the realm of unmanaged code? I ask because I'd like to implement a Dispose pattern to release all of the COM objects with Marshal.FinalReleaseComObject(comObject).

Lastly, when is a Finalizer appropriate for unmanaged resources? The Microsoft documentation talks about Files, Network Connections and other unmanaged resources. It's a bit confusing for me because the Disposal pattern provided in the Microsoft documentation shows disposing of Unmanaged Resources in the Dispose method.

Would I call the Marshal.FinalReleaseComObject method in a Dispose or Finalizer? Thanks

UPDATE: There's some debate about whether to call ReleaseComObject vs FinalReleaseComObject. I'm under the impression that FinalReleaseComObject is the better option. As it turns out, FinalReleaseComObject calls ReleaseComObject itself in a loop until the reference count for that object is 0.

https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.releasecomobject?view=net-5.0

Therefore, use the ReleaseComObject only if it is absolutely required. If you want to call this method to ensure that a COM component is released at a determined time, consider using the FinalReleaseComObject method instead. FinalReleaseComObject will release the underlying COM component regardless of how many times it has re-entered the CLR. The internal reference count of the RCW is incremented by one every time the COM component re-enters the CLR. Therefore, you could call the ReleaseComObject method in a loop until the value returned is zero. This achieves the same result as the FinalReleaseComObject method.

From reference source:

public static int FinalReleaseComObject (object o)
{
    while (ReleaseComObject (o) != 0) ;
    return 0;
}

This is the pattern I'm currently using. I'm open to changing it based on the answer given.

protected virtual void Dispose(bool disposing)
{
    if (!this.disposedValue)
    {
        if (disposing)
        {
            // TODO: dispose managed state (managed objects)
            if(document != null)
            {
                document.Dispose();
            }
        }           
        // TODO: free unmanaged resources (unmanaged objects) and override finalizer           
        if (this.application != null) {
            this.CloseContext();
            Marshal.FinalReleaseComObject(this.application);
        }
        // TODO: set large fields to null            
        this.disposedValue = true;
    }
}

// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
~AdobeContext()
{
    // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
    
    Dispose(disposing: false);
    Console.WriteLine("AdobeContext Finalizer run");
}

public void Dispose()
{
    // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
    Dispose(disposing: true);
    GC.SuppressFinalize(this);
    Console.WriteLine("AdobeContext Dispose run");
}
Charles Owen
  • 2,403
  • 1
  • 14
  • 25

1 Answers1

2

Are all COM objects in the realm of unmanaged code?

No, you can write COM objects in .NET as well.

release all of the COM objects with Marshal.FinalReleaseComObject(comObject).

IMHO, using Marshal.FinalReleaseComObject(comObject) is not 100% safe. Imagine that the COM object is a Singleton (which is not likely) and runs in an EXE-Server (which is also not likely), you could destroy the COM object for all applications currently accessing it.

Just call Marshal.ReleaseComObject() and rely on the reference counting.

when is a Finalizer appropriate for unmanaged resources?

Never. Really, that's why need a proper use of the IDisposable pattern. (Highly recommended read!)

Would I call the Marshal.FinalReleaseComObject method in a Dispose or Finalizer?

It is not safe to make calls on an RCW on the finalizer thread unless some precautions were taken.

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
  • What is did was create a finalizer but it only calls Dispose(false). In my Dispose method I called Marshal.FinalReleaseComObject() outside of the if(disposing) block. So the finalizer is not directly calling Marshal.FinalReleaseComObject. I noticed in my case when I failed to dispose, the finalizers ran and cleared up the memory of the Adobe document. This would probably have issues in a multi-threaded scenario because a method must be thread safe to be called by a finalizer. I remember running into this kind of issue in a multi-threaded service I created and I eliminated the finalizers. – Charles Owen Jul 11 '21 at 18:18