Search code examples
c#.netgarbage-collectionvstocom-object

Releasing COM Object in Constructor Parameter


I am developing a VSTO addin where now I am looking to optimize it. In my code, I does something

public class ExtraOrdinaryClass
{
    public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
    {
        tSheetName            = someGoodSheet.Name;
        tDesignSheet          = someGoodSheet;
    }
}

I just got to know I should release all the COM Objects, but I am searching for a proper way to release the someGoodSheet object in a proper way. I suspect if I do something like below is efficient

public class ExtraOrdinaryClass
{
    public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
    {
        tSheetName            = someGoodSheet.Name;
        tDesignSheet          = someGoodSheet;
        Marshal.ReleaseComObject(someGoodSheet);
        someGoodSheet = null;
    }
}

Can anyone help me if I am doing it effectively and tell me when parameter objects are collected by garbage collector?


Solution

  • I just got to know I should release all the COM Objects, but I am searching for a proper way to release the someGoodSheet object in a proper way

    It is not necessary to worry about releasing COM objects because you are writing a VSTO add-in. VSTO add-in are in-process COM libraries loaded by the COM application, in this case Excel. The COM object represented by the Excel worksheet was created by Excel and so it has ownership. Attempting to manually release (via Marshal.ReleaseComObject) or reduce the reference count inordinately when you still have a managed reference to it (as in your second example), may crash Excel and/or your application.

    Had you been writing a stand-alone process which say launched Excel via COM and fiddled with a few COM objects, then yes, you would need to ensure COM objects are released appropriately.

    i.e. do not use this in your constructor:

    Marshal.ReleaseComObject(someGoodSheet);
    

    Because you are essentially saying to COM "I'm finished with it" and yet you are not because you still have a managed referenced to it via tDesignSheet. COM/Excel may choose to get rid of the worksheet from under you. The next time you go to access tDesignSheet you may encounter an exception.


    Calling Marshal.ReleaseComObject on a parameter that is copied to a field is sort of like using a Font after you called Dispose() - both will lead to the undesirable situation where an obliterated object is accessed. (in the case of COM I assume the reference count reached 0)


    Also it is not necessary to call this (because you have a reference to the same object anyway):

    someGoodSheet = null;
    

    Your original code (shown below) is fine:

    public class ExtraOrdinaryClass
    {
        public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
        {
            tSheetName            = someGoodSheet.Name;  // you arguably don't need this (just read tDesignSheet.Name)          
            tDesignSheet          = someGoodSheet;
        }
    }
    

    and tell me when parameter objects are collected by garbage collector?

    I would say that in this case the .NET parameter would not be collected because now you have an additional reference to it in tDesignSheet inside ExtraOrdinaryClass. You would either need to set tDesignSheet to null and wait for the next time the GC runs, whenever that is.

    Even if the .NET object is collected, .NET I suspect is smart enough to know that the COM object may still be used by other native COM clients such as Excel itself.

    So, just because your .NET now-disposed objects no longer refer to the COM object, you may find the COM object could well be still active. e.g. the worksheet is still open.