Excel Process不会以Excel-Introp终止

时间:2015-07-20 18:48:51

标签: c# excel-interop

我一直在尝试将Excel工作表中的数据导入DataTable。问题是,在我完成后,excel不会终止该过程。我不希望使用System.Diagnostics按进程名称终止,因为该方法将终止所有excel实例而不是应用程序创建的实例。我知道这个问题多次发布在这里,但没有一个解决方案似乎适合我。我可能错过了一些东西而且看不到它。

下面是我的代码:

    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;

        }  
    }

2 个答案:

答案 0 :(得分:1)

编写一个方法来释放对象,例如这个例子

        ReleaseOfficeObject(range);
        ReleaseOfficeObject(xlWorkSheetDATA);
        ReleaseOfficeObject(xlSheets);
        xlWorkBook.Close(false);
        ReleaseOfficeObject(xlWorkBook);
        xlWorkBook = null;
        xlWorkBooks.Close();
        ReleaseOfficeObject(xlWorkBooks);
        xlWorkBooks = null;
        xlApp.Quit();
        ReleaseOfficeObject(xlApp);

然后把它叫做

class CellVisitor
{
public:  
  virtual accept(Cell* cell) = 0;
};

class Cell
{
public:
  virtual void visit() = 0;

};

class ContainerCell : public Cell
{
private:
  std::vector<std::unique_ptr<Cell>> cells;

public:
  void addCell(...) { ... }

  void visit(CellVisitor* visitor) override
  {
    visitor->accept(this);
    for (auto& cell : cells)
      cell->visit();
  }
};

class IntegerCell : public Cell
{
private:
  std::vector<int> data;

public:
  void visit(CellVisitor* visitor) override
  {
    visitor->accept(this);
  }
}

答案 1 :(得分:1)

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

在调试版本中,您必须小心使用此类代码。方法中的引用被人为地保持活动直到方法结束,以便它们仍然可以在调试器中访问。这意味着可能无法通过在该方法中调用GC来清除本地xlApp变量。

你应该运行两次垃圾收集,第一个集合中的引用周期可能会中断,但你需要第二个集合来确保为这些引用完成所有清理。

要避免此问题,您可以遵循以下模式:

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! 
}