Excel 进程不会以 Excel-Introp 终止
Excel Process will not terminate with Excel-Introp
我一直在尝试将数据从 excel sheet 导入 DataTable
。问题是在我完成后,excel 不会终止进程。我不想使用 System.Diagnostics
按进程名称终止,因为该方法会终止所有 excel 实例,而不是应用程序创建的实例。我知道这个问题之前曾多次在此处发布,但 none 的解决方案似乎对我有用。我可能遗漏了什么,看不到它。
下面是我的代码:
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel WorkBooks (*.xlsx)|*.xlsx";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
string empRange = "D4";
string emptxid = "K4";
object oMissing = System.Reflection.Missing.Value;
string path = openFileDialog1.FileName;
oExcel.Application xlApp;
oExcel.Workbooks xlWorkBooks;
oExcel.Workbook xlWorkBook;
oExcel.Sheets xlSheets;
oExcel.Worksheet xlWorkSheetDATA;
oExcel.Worksheet xlWorkSheetEMP;
oExcel.Range range;
xlApp = new oExcel.Application();
xlWorkBooks = xlApp.Workbooks;
xlWorkBook = xlWorkBooks.Open(path);
xlSheets = xlWorkBook.Worksheets;
xlWorkSheetDATA = (oExcel.Worksheet)xlSheets.get_Item(DATASheetName);
xlWorkSheetEMP = (oExcel.Worksheet)xlSheets.get_Item(EMPSheetName);
xlWorkSheetEMP.Activate();
range = xlWorkSheetEMP.get_Range(empRange, empRange);
xlWorkSheetDATA.Activate();
range = xlWorkSheetDATA.get_Range(emptxid, emptxid);
xlApp.DisplayAlerts = false;
xlWorkSheetDATA.Columns.ClearFormats();
xlWorkSheetDATA.Rows.ClearFormats();
int iTotalColumns = xlWorkSheetDATA.UsedRange.Columns.Count;
int iTotalRows = xlWorkSheetDATA.UsedRange.Rows.Count;
xlApp.Visible = true;
DataTable dt = new DataTable();
addColumns(iTotalColumns, dt);
insertIntoDataTable(iTotalRows, dt, path);
//clean-up
System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
range = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetEMP);
xlWorkSheetEMP = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetDATA);
xlWorkSheetDATA = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets);
xlSheets = null;
xlWorkBook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBook);
xlWorkBook = null;
xlWorkBooks.Close();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBooks);
xlWorkBooks = null;
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
xlApp = null;
}
}
写一个释放对象的方法如本例
private void ReleaseOfficeObject(object o)
{
Marshal.ReleaseComObject(o);
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(o);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
然后调用它
ReleaseOfficeObject(range);
ReleaseOfficeObject(xlWorkSheetDATA);
ReleaseOfficeObject(xlSheets);
xlWorkBook.Close(false);
ReleaseOfficeObject(xlWorkBook);
xlWorkBook = null;
xlWorkBooks.Close();
ReleaseOfficeObject(xlWorkBooks);
xlWorkBooks = null;
xlApp.Quit();
ReleaseOfficeObject(xlApp);
您永远不需要在此上下文中调用 Marshal.ReleaseComObject
。 运行time 完全能够跟踪 COM 对象并在它们不再被引用时释放它们。调用 Marshal.ReleaseComObject
是一种令人困惑的反模式,令人遗憾的是,甚至某些 Microsoft 文档也错误地暗示了这一点。您也不必将 local 变量设置为 null - 当方法完成时,局部变量中的引用将超出范围。
在调试版本中,您必须小心处理此类代码。方法中的引用被人为地保持活动状态,直到方法结束,以便它们仍然可以在调试器中访问。这意味着您的本地 xlApp
变量可能无法通过在该方法中调用 GC 来清理。
您应该 运行 两次垃圾回收,第一次回收可能会破坏引用循环,但您需要第二次回收以确保对这些引用完成所有清理工作。
为避免此问题,您可以遵循如下模式:
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel WorkBooks (*.xlsx)|*.xlsx";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
string path = openFileDialog1.FileName;
// Call another method that wraps all the Excel COM stuff
// That prevents debug builds from confusing the lifetime of COM references
DoExcelStuff(path);
// When back here, all the Excel references should be out of scope
// Run the GC (twice) to clean up all COM references
// (This can be pulled out into a helper method somewhere)
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
private void DoExcelStuff(string path)
{
// All your Excel COM interop goes here
string empRange = "D4";
string emptxid = "K4";
object oMissing = System.Reflection.Missing.Value;
// ... variable declarations
xlApp = new oExcel.Application();
// More xlApp, workbooks etc...
// Done - tell Excel to close
xlApp.Quit();
// No need for Marshal.ReleaseComObject(...)
// or setting variables to null here!
}
我一直在尝试将数据从 excel sheet 导入 DataTable
。问题是在我完成后,excel 不会终止进程。我不想使用 System.Diagnostics
按进程名称终止,因为该方法会终止所有 excel 实例,而不是应用程序创建的实例。我知道这个问题之前曾多次在此处发布,但 none 的解决方案似乎对我有用。我可能遗漏了什么,看不到它。
下面是我的代码:
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel WorkBooks (*.xlsx)|*.xlsx";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
string empRange = "D4";
string emptxid = "K4";
object oMissing = System.Reflection.Missing.Value;
string path = openFileDialog1.FileName;
oExcel.Application xlApp;
oExcel.Workbooks xlWorkBooks;
oExcel.Workbook xlWorkBook;
oExcel.Sheets xlSheets;
oExcel.Worksheet xlWorkSheetDATA;
oExcel.Worksheet xlWorkSheetEMP;
oExcel.Range range;
xlApp = new oExcel.Application();
xlWorkBooks = xlApp.Workbooks;
xlWorkBook = xlWorkBooks.Open(path);
xlSheets = xlWorkBook.Worksheets;
xlWorkSheetDATA = (oExcel.Worksheet)xlSheets.get_Item(DATASheetName);
xlWorkSheetEMP = (oExcel.Worksheet)xlSheets.get_Item(EMPSheetName);
xlWorkSheetEMP.Activate();
range = xlWorkSheetEMP.get_Range(empRange, empRange);
xlWorkSheetDATA.Activate();
range = xlWorkSheetDATA.get_Range(emptxid, emptxid);
xlApp.DisplayAlerts = false;
xlWorkSheetDATA.Columns.ClearFormats();
xlWorkSheetDATA.Rows.ClearFormats();
int iTotalColumns = xlWorkSheetDATA.UsedRange.Columns.Count;
int iTotalRows = xlWorkSheetDATA.UsedRange.Rows.Count;
xlApp.Visible = true;
DataTable dt = new DataTable();
addColumns(iTotalColumns, dt);
insertIntoDataTable(iTotalRows, dt, path);
//clean-up
System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
range = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetEMP);
xlWorkSheetEMP = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetDATA);
xlWorkSheetDATA = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets);
xlSheets = null;
xlWorkBook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBook);
xlWorkBook = null;
xlWorkBooks.Close();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBooks);
xlWorkBooks = null;
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
xlApp = null;
}
}
写一个释放对象的方法如本例
private void ReleaseOfficeObject(object o)
{
Marshal.ReleaseComObject(o);
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(o);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
然后调用它
ReleaseOfficeObject(range);
ReleaseOfficeObject(xlWorkSheetDATA);
ReleaseOfficeObject(xlSheets);
xlWorkBook.Close(false);
ReleaseOfficeObject(xlWorkBook);
xlWorkBook = null;
xlWorkBooks.Close();
ReleaseOfficeObject(xlWorkBooks);
xlWorkBooks = null;
xlApp.Quit();
ReleaseOfficeObject(xlApp);
您永远不需要在此上下文中调用 Marshal.ReleaseComObject
。 运行time 完全能够跟踪 COM 对象并在它们不再被引用时释放它们。调用 Marshal.ReleaseComObject
是一种令人困惑的反模式,令人遗憾的是,甚至某些 Microsoft 文档也错误地暗示了这一点。您也不必将 local 变量设置为 null - 当方法完成时,局部变量中的引用将超出范围。
在调试版本中,您必须小心处理此类代码。方法中的引用被人为地保持活动状态,直到方法结束,以便它们仍然可以在调试器中访问。这意味着您的本地 xlApp
变量可能无法通过在该方法中调用 GC 来清理。
您应该 运行 两次垃圾回收,第一次回收可能会破坏引用循环,但您需要第二次回收以确保对这些引用完成所有清理工作。
为避免此问题,您可以遵循如下模式:
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel WorkBooks (*.xlsx)|*.xlsx";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
string path = openFileDialog1.FileName;
// Call another method that wraps all the Excel COM stuff
// That prevents debug builds from confusing the lifetime of COM references
DoExcelStuff(path);
// When back here, all the Excel references should be out of scope
// Run the GC (twice) to clean up all COM references
// (This can be pulled out into a helper method somewhere)
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
private void DoExcelStuff(string path)
{
// All your Excel COM interop goes here
string empRange = "D4";
string emptxid = "K4";
object oMissing = System.Reflection.Missing.Value;
// ... variable declarations
xlApp = new oExcel.Application();
// More xlApp, workbooks etc...
// Done - tell Excel to close
xlApp.Quit();
// No need for Marshal.ReleaseComObject(...)
// or setting variables to null here!
}