2015-07-20 102 views
0

我一直在尝试从Excel表导入数据到DataTable。问题是,我完成后,Excel不会终止进程。我不想通过使用System.Diagnostics的进程名称来终止,因为该方法会终止所有的excel实例,而不是应用程序创建的实例。我知道这个问题在多次出现在这里,但没有一个解决方案似乎适用于我。我可能错过了一些东西,看不到它。Excel进程不会终止于Excel-Introp

下面

是我的代码:

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; 

     } 
    } 
+0

我从来没有找到一个方法来做到这一点...你当你调用互操作,是调用这些服务做什么通过Excel过程。通常情况下,GC将释放资源,但Excel不会收集垃圾收集的内存。 –

+0

http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects – josh

回答

1

写来释放对象的方法,如本例中

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

你永远不需要调用Marshal.ReleaseComObject在这方面。运行时完全能够跟踪COM对象,并在它们不再被引用时释放它们。调用Marshal.ReleaseComObject是一个令人困惑的反模式,即使一些微软文档错误地暗示了这种模式。您也不必将本地变量设置为空 - 当方法完成时,局部变量中的引用将超出范围。

在调试版本中,你必须小心这种代码。一个方法中的引用被人工保存直到方法结束,以便它们仍然可以在调试器中访问。这意味着您的本地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! 
}