views:

323

answers:

3

Hi all,

I'm writing a Excel class using Microsoft.Interropt.Excel DLL. I finish all function but I have an error in my Destructor. I Want to save all changes to my file and I want to release all source. I want to all of them in my destructor. But In my destructor, Excel.ApplicationClass, Workbook and Worksheet objects are fill by an Exception which have message "COM object that has been separated from its underlying RCW cannot be used." So I can't save nothing, close nothing because ı can't access workbook or worksheet object.

Can't I access the class private members in Destructor?

A: 

No, you should not be accessing any managed objects in a destructor: this includes COM RCWs.

Instead, implement the standard IDisposable pattern, and release your COM objects in the Dispose(bool) method like you would a disposable managed object.

Joe
+5  A: 

The closest thing that .NET has to a destructor, is what .NET calls a finalizer. The main difference is that a destructor typically has deterministic finalization (say, when the reference count on the object becomes zero), while a .NET finalizer is called at an undetermined time after the object is no longer referenced. This is handled by the .NET Garbage Collector using a roots-tracing procedure, rather than using simple reference counting.

One of the best articles on this is Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework. For more on finalizers specifically, see the article Finalize Methods and Destructors in MSDN.

Can't I access the class private members in Destructor?

No, you cannot do so safely.

What is happening in your case is that when your object is no longer directly or indirectly referenced by a root, the COM objects that your object references -- that is, the objects referenced by your private fields -- are also not referenced by a root either. (Being reference by your object's fields does not keep these COM objects alive because your object is no longer referenced by, or traced from, a root, and, therefore, the COM objects do not trace from a root either.) So your object and all the COM objects it references are all ready to be garbage collected at the same time. Some time later, the garbage collector will cleanup your object and call its finalizer, as it will also do with the COM objects, each of which is really a Runtime Callable Wrapper (RCW).

The trouble is that not only is the timing as to when these objects are to be garbage collected uncertain, but the order in which the finalizers are called is also nondeterministic. In this case, a Runtime Callable Wrapper also has a finalizer, which calls Marshal.ReleaseComObject on itself, which has the result of decrementing the reference count on the COM side of the fence so that this COM object can be released. But since the order in which the finalizers are called is uncertain, it is very possible that the finalizers for the COM objects that your object references will fire before the finalizer for your object. So the code within your finalizer could work sometimes, but, most of the time, one or more of the Runtime Callable Wrappers that your object references will have already had their finalizers called, and the underlying COM object will have been released, before your finalizer gets to execute its code.

In short, you should avoid using finalizers in general, and you should never access reference types from within a finalizer, because these reference types may have already been finalized.

To remedy your situation, I would consider two different possibilities:

  1. Dispose of the COM objects within the same method that creates them. I have a couple of discussions on this here and here.

  2. Enable deterministic disposal of your object by making use of the IDisposable interface, instead of relying on the non-deterministic finalizer.

For articles on how to implement the IDisposable pattern, see:

-- Mike

Mike Rosenblum
A: 

I am not sure if I am coding something wrong--tried to follow the examples here. I found that when I take advantage of the IDisposable pattern everything works unless I need to handle workbook events.

In my scenario the user can close the Workbook before they close the app. I have declared Excel object WithEvents and coded the WorkbookBeforeClose handler to meet the requirements.

In this scenario I get the "COM object that has been separated from its underlying RCW cannot be used" error when I go to close my app (and I have already closed the Excel). The error is happening in the Finalize when it calls Dispose(False).

The problem goes away if I leave the Excel object declared with events but do not code any handlers.

In my Dispose I had to swallow the error for my Workbooks.Close and Quit as they were the statements that caused the error.

sdmcnitt