我应该如何声明Microsoft.Office.Interop.Excel.Worksheet,以便我可以在C#中关闭它?

时间:2016-04-25 14:51:15

标签: c# office-interop com-interop excel-interop

我很难确保我的COM对象正在关闭,所以我不会让EXCEL.EXE进程在后台运行。我已经知道声明的双点和链接是坏的,因为这可能会让COM对象徘徊。

有没有人知道如何修复这行代码,以便我可以正确关闭并释放工作表COM对象?

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

编辑:当我完成执行此行以关闭打开的Excel对象时,我尝试使用app.Quit(),但这不起作用。如果我在此行之前调用它,则app.Quit()有效。

1 个答案:

答案 0 :(得分:4)

首先,为了让EXCEL.EXE进程退出,您不必显式释放您使用的所有COM对象。当您获取对COM对象的另一个引用时,.NET运行时为您隐式创建的运行时可调用包装程序(RCW)将由GC收集,并释放基础COM对象。

您所要做的就是调用Quit方法,释放RCWs引用并让GC收集它们。

//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;

其次,如果你留下所谓的双点代码行,你就不会产生任何内存泄漏。垃圾收集器将收集RCW并在某些时候释放COM对象,即当它找到正确的时候。

最后,如果您希望显式释放RCW和相应的COM对象以不惜一切代价最小化内存压力,您可以在释放对它们的引用后显式调用GC来收集RCW,或者甚至显式释放底层COM GC收集RCW之前的对象。但要小心。最后一种方式让你负责这样一个事实,即剩下的RCW在此之后永远不会被使用,或者你将会有例外。

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);

请记住。如果你手动释放COM引用而不是EXCEL.EXE生命周期不依赖于RCW,你可以在你喜欢的时候使它们无效...

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

在最明确的情况下,不要忘记检查Marshal.ReleaseComObject的返回。如果它不为零,那么除了刚刚发布的RCW之外,还有其他人持有对你的底层COM对象的COM引用。