C# excel 互操作进程在崩溃后仍然存在
C# excel interop process persists after crash
我最近刚开始使用 C# 中的 Excel Interop,运行 遇到了一个问题,如果我的应用程序崩溃,Excel 进程会持续存在。 (为什么 进程崩溃是我正在调查的另一个问题。)
我 认为 我正在正确释放 COM 对象,因为如果我的应用程序成功完成,一切都会清理干净。只有当它崩溃或者我在调试过程中恰好退出时才会留下Excel进程。
当然我知道当它崩溃时,COM 对象没有被清理。我不知道该如何处理。
这里有一些伪代码,希望能演示我在做什么(真正的代码相当长。)
它应该 1) 打开现有的 excel 文件,2) 访问文件中的特定工作表,3) 插入大量行,4) 向这些行添加值,5) 关闭并全部保存。
我做错了什么?
// Open excel file
try {
myExcelApp = new Excel.Application();
myExcelApp.Visible = false;
myExcelApp.DisplayAlerts = false;
myExcelWorkbook = (Excel.Workbook)myExcelApp.Workbooks.Open(excelFile);
} catch (Exception ex) {
string msg = "Error:Failed opening Excel File " + excelFile + ": " + ex.Message;
throw new Exception(msg);
}
// ---- some other stuff here. ----
foreach ( var toolWorkSheetName in workSheetsList ){
// Init
Excel.Worksheet xlWorksheet = null;
Excel.Range xlRange = null;
// Get specific worksheet from workbook
try {
xlWorksheet = (Excel.Worksheet)myExcelWorkbook.Worksheets[toolWorkSheetName];
} catch (Exception ex) {
string msg = "Error:Could not open worksheet in " + toolWorkSheetName + ": " + ex.Message;
throw new Exception(msg);
}
// First scan existing template for insertion row & number of rows to insert
xlRange = xlWorksheet.UsedRange;
object[,] values = (object[,])xlRange.Value2;
Marshal.ReleaseComObject(xlRange); // Release local com objects
int colCount = values.GetLength(1);
values = null;
// ---- Determine the following: -----
// insertRow =~ 3;
// nLinesToInsert =~ 63233;
// colCount =~ 400;
// Insert a range of rows for the values
Excel.Range range = xlWorksheet.Range[xlWorksheet.Cells[insertRow, 1], xlWorksheet.Cells[insertRow + nLinesToInsert - 1, colCount]];
range.Insert(Excel.XlInsertShiftDirection.xlShiftDown, Excel.XlInsertFormatOrigin.xlFormatFromLeftOrAbove);
Marshal.ReleaseComObject(range);
values = new object[nLinesToInsert, colCount];
// ---- populate the new values array ----
// Insert the values at the target rows.
Excel.Range startCell = (Excel.Range)xlWorksheet.Cells[insertRow, 1];
Excel.Range endCell = (Excel.Range)xlWorksheet.Cells[insertRow + nCsvInsertRows - 1, nColsDo];
Excel.Range writeRange = xlWorksheet.Range[startCell, endCell];
writeRange.Value2 = values;
Marshal.ReleaseComObject(writeRange);
Marshal.ReleaseComObject(endCell);
Marshal.ReleaseComObject(startCell);
// Release local com objects
Marshal.ReleaseComObject(xlWorksheet);
}
//cleanup (NB: Does this need to be done? Does it need to be done here?)
GC.Collect();
GC.WaitForPendingFinalizers();
// Save
object misValue = System.Reflection.Missing.Value;
myExcelWorkbook.SaveAs(outFile, Microsoft.Office.Interop.Excel.XlFileFormat.xlOpenXMLWorkbook, misValue,
misValue, misValue, misValue, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive,
misValue, misValue, misValue, misValue, misValue);
myExcelWorkbook.Close();
myExcelApp.Quit();
//close and release
Marshal.ReleaseComObject(myExcelWorkbook);
myExcelWorkbook = null;
//quit and release
Marshal.ReleaseComObject(myExcelApp);
myExcelApp = null;
您正在 try-catch
块之外释放对象。在 catch 中,即使有一条新消息,您也会创建一个新的异常。当您从 catch 块创建新异常时,您原来的异常就消失了。被认为是一种不好的做法。
您必须释放 catch
或 finally
块内的对象。根据您的代码,您的对象在崩溃后仍然存在。
顺便说一句,要与 Excel 一起工作,我会推荐 EPPlus 库。它将执行您需要的所有操作,而无需在服务器上安装 Excel(再次是错误做法)。
更新
要清理所有对象:
System.Runtime.InteropServices.Marshal.ReleaseComObject(startCell );
System.Runtime.InteropServices.Marshal.ReleaseComObject(endCell);
System.Runtime.InteropServices.Marshal.ReleaseComObject(writeRange);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange );
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorksheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange );
startCell = null;
endCell = null;
writeRange = null;
myExcelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(myExcelApp);
myExcelApp = null;
myExcelWorkbook = null;
System.GC.Collect();
GC.WaitForPendingFinalizers();
在你的 try catch 中,你应该关闭 Excel。像这样
try{
//Some code
}
catch{
Marshal.ReleaseComObject(xlWorksheet);
myExcelWorkbook.Close();
myExcelApp.Quit();
}
因为它失败时不会关闭 Excel。
我最近刚开始使用 C# 中的 Excel Interop,运行 遇到了一个问题,如果我的应用程序崩溃,Excel 进程会持续存在。 (为什么 进程崩溃是我正在调查的另一个问题。)
我 认为 我正在正确释放 COM 对象,因为如果我的应用程序成功完成,一切都会清理干净。只有当它崩溃或者我在调试过程中恰好退出时才会留下Excel进程。
当然我知道当它崩溃时,COM 对象没有被清理。我不知道该如何处理。
这里有一些伪代码,希望能演示我在做什么(真正的代码相当长。)
它应该 1) 打开现有的 excel 文件,2) 访问文件中的特定工作表,3) 插入大量行,4) 向这些行添加值,5) 关闭并全部保存。
我做错了什么?
// Open excel file
try {
myExcelApp = new Excel.Application();
myExcelApp.Visible = false;
myExcelApp.DisplayAlerts = false;
myExcelWorkbook = (Excel.Workbook)myExcelApp.Workbooks.Open(excelFile);
} catch (Exception ex) {
string msg = "Error:Failed opening Excel File " + excelFile + ": " + ex.Message;
throw new Exception(msg);
}
// ---- some other stuff here. ----
foreach ( var toolWorkSheetName in workSheetsList ){
// Init
Excel.Worksheet xlWorksheet = null;
Excel.Range xlRange = null;
// Get specific worksheet from workbook
try {
xlWorksheet = (Excel.Worksheet)myExcelWorkbook.Worksheets[toolWorkSheetName];
} catch (Exception ex) {
string msg = "Error:Could not open worksheet in " + toolWorkSheetName + ": " + ex.Message;
throw new Exception(msg);
}
// First scan existing template for insertion row & number of rows to insert
xlRange = xlWorksheet.UsedRange;
object[,] values = (object[,])xlRange.Value2;
Marshal.ReleaseComObject(xlRange); // Release local com objects
int colCount = values.GetLength(1);
values = null;
// ---- Determine the following: -----
// insertRow =~ 3;
// nLinesToInsert =~ 63233;
// colCount =~ 400;
// Insert a range of rows for the values
Excel.Range range = xlWorksheet.Range[xlWorksheet.Cells[insertRow, 1], xlWorksheet.Cells[insertRow + nLinesToInsert - 1, colCount]];
range.Insert(Excel.XlInsertShiftDirection.xlShiftDown, Excel.XlInsertFormatOrigin.xlFormatFromLeftOrAbove);
Marshal.ReleaseComObject(range);
values = new object[nLinesToInsert, colCount];
// ---- populate the new values array ----
// Insert the values at the target rows.
Excel.Range startCell = (Excel.Range)xlWorksheet.Cells[insertRow, 1];
Excel.Range endCell = (Excel.Range)xlWorksheet.Cells[insertRow + nCsvInsertRows - 1, nColsDo];
Excel.Range writeRange = xlWorksheet.Range[startCell, endCell];
writeRange.Value2 = values;
Marshal.ReleaseComObject(writeRange);
Marshal.ReleaseComObject(endCell);
Marshal.ReleaseComObject(startCell);
// Release local com objects
Marshal.ReleaseComObject(xlWorksheet);
}
//cleanup (NB: Does this need to be done? Does it need to be done here?)
GC.Collect();
GC.WaitForPendingFinalizers();
// Save
object misValue = System.Reflection.Missing.Value;
myExcelWorkbook.SaveAs(outFile, Microsoft.Office.Interop.Excel.XlFileFormat.xlOpenXMLWorkbook, misValue,
misValue, misValue, misValue, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive,
misValue, misValue, misValue, misValue, misValue);
myExcelWorkbook.Close();
myExcelApp.Quit();
//close and release
Marshal.ReleaseComObject(myExcelWorkbook);
myExcelWorkbook = null;
//quit and release
Marshal.ReleaseComObject(myExcelApp);
myExcelApp = null;
您正在 try-catch
块之外释放对象。在 catch 中,即使有一条新消息,您也会创建一个新的异常。当您从 catch 块创建新异常时,您原来的异常就消失了。被认为是一种不好的做法。
您必须释放 catch
或 finally
块内的对象。根据您的代码,您的对象在崩溃后仍然存在。
顺便说一句,要与 Excel 一起工作,我会推荐 EPPlus 库。它将执行您需要的所有操作,而无需在服务器上安装 Excel(再次是错误做法)。
更新
要清理所有对象:
System.Runtime.InteropServices.Marshal.ReleaseComObject(startCell );
System.Runtime.InteropServices.Marshal.ReleaseComObject(endCell);
System.Runtime.InteropServices.Marshal.ReleaseComObject(writeRange);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange );
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorksheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange );
startCell = null;
endCell = null;
writeRange = null;
myExcelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(myExcelApp);
myExcelApp = null;
myExcelWorkbook = null;
System.GC.Collect();
GC.WaitForPendingFinalizers();
在你的 try catch 中,你应该关闭 Excel。像这样
try{
//Some code
}
catch{
Marshal.ReleaseComObject(xlWorksheet);
myExcelWorkbook.Close();
myExcelApp.Quit();
}
因为它失败时不会关闭 Excel。