gjvdkamp
gjvdkamp

Reputation: 10516

Get RCW's from Marshal to see which object is being held

I have an Excel add-in that needs a lot of long running references to Excel native objects. Every now and then when a user closes Excel it 'hangs' keeping an Excel process live in the background.

There's quite been some debate between trusting the GC and doing manual cleanup. Orignially I trusted the GC, also doing this when the add-in unloads for good measure:

do {
    GC.Collect();
    GC.WaitForPendingFinalizers();
}
while (Marshal.AreComObjectsAvailableForCleanup());

but now the issue keeps popping up at random and I'm finding it hard to figure out what it could be. Also inserted Marshal.ReleaseComObject everywhere but every now and then it still hangs.

So I was thinking, if I can ask Marshal.AreComObjectsAvailableForCleanup then apparently there's a central list of RCW's being kept by Marshal?

Is there a way to get to this list and display information about the native objects being held? That would be really helpful in finding the offender. I looked over the public methods but there does not seem to be anything available. Looking at the source the trail goes dead at this call:

[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern int InternalReleaseComObject(Object o);

But maybe someone here knows a way to continue and list the objects?

Any other suggestions to get Excel to shut down properly also welcome of course.

Upvotes: 1

Views: 134

Answers (1)

roland kapl
roland kapl

Reputation: 73

I had a similar problem with Excel crashing after being closed and for me it was also related to COM Objects not being released properly on shutdown.

I have followed Goverts suggestion to use the OnDisconnection (here using the Ribbon deriving from ExcelComAddIn which implents IDTExtensibility2) method (Functions.StatusCollection and DBModifDefColl are collections of dynamically allocated objects containing references to excel ranges, which are then explicitly released using Marshal.ReleaseComObject):

Public Class MenuHandler
  Inherits CustomUI.ExcelRibbon

  Public Overrides Sub OnDisconnection(RemoveMode As ExcelDna.Integration.Extensibility.ext_DisconnectMode, ByRef custom As Array)
    For Each aKey As String In Functions.StatusCollection.Keys
        Dim clearRange As Excel.Range = Functions.StatusCollection(aKey).formulaRange
        If Not IsNothing(clearRange) Then Marshal.ReleaseComObject(clearRange)
    Next
    For Each DBmodifType As String In DBModifDefColl.Keys
        For Each dbmapdefkey As String In DBModifDefColl(DBmodifType).Keys
            Dim clearRange As Excel.Range = DBModifDefColl(DBmodifType).Item(dbmapdefkey).getTargetRange()
            If Not IsNothing(clearRange) Then Marshal.ReleaseComObject(clearRange)
        Next
    Next
    Do
        GC.Collect()
        GC.WaitForPendingFinalizers()
    Loop While (Marshal.AreComObjectsAvailableForCleanup())
  End Sub
...

Once this was implemented I had no more crashes on excel shutdown...

Upvotes: 1

Related Questions