为什么Excel com对象不从内存中释放,如果我检查它为空
本文关键字:如果 检查 释放 com Excel 对象 内存 为什么 | 更新日期: 2023-09-27 17:50:18
我继承了一个现有的应用程序来维护,并遇到了一个障碍:它不会从内存中释放Excel对象,一旦它完成。我在这里抽出了相关的代码。这是一个使用COM Interop创建Excel工作簿的方法:
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
...
private void CreateWorkbook(string workingDirectory, string fileName, object[,] cellValues)
{
Excel.Application excel = null;
Excel.Workbooks workbooks = null;
Excel.Workbook workbook = null;
Excel.Sheets worksheets = null;
Excel.Worksheet worksheet = null;
Excel.Range startRange = null;
Excel.Range endRange = null;
Excel.Range selectedRange = null;
try
{
excel = new Excel.Application() { DisplayAlerts = false, Visible = false, ScreenUpdating = false, EnableAutoComplete = false };
// Statement which stops the excel instance exiting:
if(excel == null)
{
MessageBox.Show("Excel could not be started, please check your machine has office 2013 installed.");
return;
}
workbooks = excel.Workbooks;
workbook = workbooks.Add(Type.Missing);
worksheets = workbook.Sheets;
worksheet = (Excel.Worksheet)worksheets[1];
startCellRange = (Excel.Range)worksheet.Cells[1,1];
endCellRange = (Excel.Range)worksheet.Cells[1 + cellValues.GetLength(0), 1 + cellValues.GetLength(1)];
selectedCells = worksheet.Range[startCellRange, endCellRange];
selectedCells.Value2 = cellValues;
workbook.SaveAs(workingDirectory + fileName, Excel.XlFileFormat.xlOpenXMLWorkbook, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlExclusive, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
workbook.Close(true, Type.Missing, Type.Missing);
excel.Quit();
}
catch(Exception ex)
{
try
{
// Catch methods should ideally never throw exceptions, so try close the workbook and quit the excel instance in a try/catch block:
workbook.Close(false, Type.Missing, Type.Missing);
excel.Quit();
}
catch { }
MessageBox.Show("An error was encountered while trying to create the excel workbook.'n" + ex.Message + (ex.InnerException == null ? "" : "'n" + ex.InnerException.Message));
}
finally
{
// Now clean up the com interop stuff:
// ===================================
if (Marshal.IsComObject(rangeFont)) Marshal.ReleaseComObject(rangeFont);
if (Marshal.IsComObject(rangeBorders)) Marshal.ReleaseComObject(rangeBorders);
if (Marshal.IsComObject(selectedRange)) Marshal.ReleaseComObject(selectedRange);
if (Marshal.IsComObject(endRange)) Marshal.ReleaseComObject(endRange);
if (Marshal.IsComObject(startRange)) Marshal.ReleaseComObject(startRange);
if (Marshal.IsComObject(worksheet)) Marshal.ReleaseComObject(worksheet);
if (Marshal.IsComObject(worksheets)) Marshal.ReleaseComObject(worksheets);
if (Marshal.IsComObject(workbook)) Marshal.ReleaseComObject(workbook);
if (Marshal.IsComObject(workbooks)) Marshal.ReleaseComObject(workbooks);
if (Marshal.IsComObject(excel)) Marshal.ReleaseComObject(excel);
if (Marshal.IsComObject(excel)) Marshal.FinalReleaseComObject(excel);
System.Threading.Thread.Sleep(100);
GC.Collect();
GC.WaitForPendingFinalizers();
System.Threading.Thread.Sleep(500);
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
如果我在excel = new Excel.Application()...
之后注释掉if
语句,该方法可以很好地工作并处理excel对象。但是当包含if (excel == null)
测试时,excel对象不会从内存中释放出来。我一直在想为什么,但我被难住了。我知道运行时可以自动将包装器放在com对象周围(即当使用超过"2点"访问com对象时)。但检查com对象是否等于null似乎不符合运行时的逻辑,如果确实是这样的话。
那么如何正确地检查一个com对象是否为空?还有其他地方我想做检查,比如在试图关闭工作簿和退出Excel时,在catch块内。但是,我现在担心检查catch块内的excel object == null
是否会产生一个不可见的包装器,所以我使用了嵌套的try..catch块。但那感觉也不对。
问题是,发生了什么,什么是检查的最佳做法,如果你的Excel com对象实例化正确,如果我的方法是不正确的
尝试将垃圾收集器调用移动到包装器方法。这样,当您调用它时,您可以确保所有自动创建的引用都在作用域之外。
private void CreateWorkbookWithCleanup(...)
{
CreateWorkbook(...);
// Yes, we really want to call those two methods twice to make sure all
// COM objects AND all RCWs are collected.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
同样,根据我的经验,你不需要调用Marshal.ReleaseComObject
,如果你释放了所有的引用,所有的东西都被垃圾收集器释放了。也不需要睡觉。