简体   繁体   中英

How should I declare a Microsoft.Office.Interop.Excel.Worksheet so I can close it in C#?

I am having a hard time making sure my COM objects are closing so I am not leaving an EXCEL.EXE process running in the background. I have learned that the double dot and chaining of declarations is bad as this could leave COM objects hanging around.

Does anyone know how to fix this line of code so I can properly close and release the worksheet COM object?

worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workbook.Sheets[strName];

EDIT: I have tried using app.Quit() when I am done executing this line to close the open Excel object, however this does not work. app.Quit() works if I call it before this line however.

First of all, in order to let the EXCEL.EXE process quit you do not have to explicitly release all COM objects you used. The Runtime Callable Wrappers (RCW) which are implicitly created for you by the .NET runtime when you obtain another reference to a COM object are collected by GC and this releases underlying COM objects.

All you have to do is to call the Quit method, release RCWs references and let GC collect them.

//This runs the EXCEL.EXE process.
Microsoft.Office.Interop.Excel.Application app = 
    new Microsoft.Office.Interop.Excel.Application();
//This locks the excel file either for reading or writing 
//depending on parameters.
Microsoft.Office.Interop.Excel.Workbook book = app.Workbooks.Open(...);

...

//Explicitly closing the book is a good thing. EXCEL.EXE is alive 
//but the excel file gets released.
book.Close();

//This lets the EXCEL.EXE quit after you release all your references to RCWs
//and let GC collect them and thus release the underlying COM objects. 
//EXCEL.EXE does not close immediately after this though.
app.Quit();
app = null;

Second, if you leave so-called double-dot code lines be, you do not create any memory leakage. Garbage collector will collect the RCWs and will release the COM objects sometime ie when it finds it right.

And finally if you wish to release the RCWs and corresponding COM objects explicitly to minimize the memory pressure at all costs, you could explicitly call the GC to collect the RCWs after you release your references to them or even explicitly release the underlying COM objects BEFORE the GC collects the RCWs. Be careful though. This last way leaves you in responsibility for the fact that the remaining RCWs are never used after that or you are going to have an exception.

using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

Application app = new Application();
Workbooks books = clsExcelApplication.Workbooks;
Workbook book = books.Open("...");
Sheets sheets = book.Sheets;
Worksheet sheet = sheets["..."];

...

//Trying to be as explicit as we can.
book.Сlose();

//Revese order. Using FinalReleaseComObject is even more dangerous. 
//You might release an object used inside excel
//code which might lead to some dreadful internal exceptions.
int remainingRefCount;
remainingRefCount = Marshal.ReleaseComObject(sheet);
remainingRefCount = Marshal.ReleaseComObject(sheets);
remainingRefCount = Marshal.ReleaseComObject(book);
remainingRefCount = Marshal.ReleaseComObject(books);
app.Quit();
remainingRefCount = Marshal.ReleaseComObject(app);

Keep in mind. If you manually release COM references than EXCEL.EXE lifetime does not depend on the RCWs and you can nullify them when you like...

sheet = null;
sheets = null;
book = null;
books = null;
app = null;

In the most explicit case, do not forget to check the return from the Marshal.ReleaseComObject. If it is not zero, somebody else is holding a COM reference to your underlying COM object besides the RCW you just released.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM