Interop进程后无法关闭Excel.exe

pro*_*ton 28 c# excel-interop winforms

我遇到了Excel Interop的问题.

即使我重新发布实例,Excel.exe也不会关闭.

这是我的代码:

using xl = Microsoft.Office.Interop.Excel;


xl.Application excel = new xl.Application();
excel.Visible = true;
excel.ScreenUpdating = false;
if (wordFile.Contains(".csv") || wordFile.Contains(".xls"))
{
   //typeExcel become a string of the document name
   string typeExcel = wordFile.ToString();
   xl.Workbook workbook = excel.Workbooks.Open(typeExcel,
                                                oMissing,  oMissing,  oMissing,  oMissing,
                                                oMissing,  oMissing,  oMissing,  oMissing,
                                                oMissing,  oMissing,  oMissing,  oMissing,
                                                oMissing,  oMissing);
   object outputFileName = null;
   if (wordFile.Contains(".xls"))
   {
     outputFileName = wordFile.Replace(".xls", ".pdf");
   }
   else if (wordFile.Contains(".csv"))
   {
     outputFileName = wordFile.Replace(".csv", ".pdf");
   }

   workbook.ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, outputFileName, 
                                 XlFixedFormatQuality.xlQualityStandard, oMissing,
                                 oMissing, oMissing, oMissing, oMissing, oMissing);

   object saveChanges = xl.XlSaveAction.xlDoNotSaveChanges;
   ((xl._Workbook)workbook).Close(saveChanges, oMissing, oMissing);

   Marshal.ReleaseComObject(workbook);
   workbook = null;
}
Run Code Online (Sandbox Code Playgroud)

我看到它,Marshal.RealeaseComObject它应该是工作,但没有.我怎样才能解决这个问题?

谢谢.

Dzm*_*voi 67

简单规则:避免使用双点调用表达式,例如:

var workbook = excel.Workbooks.Open(/*params*/)
Run Code Online (Sandbox Code Playgroud)

...因为通过这种方式,您不仅可以创建RCW对象workbook,而且还可以创建Workbooks它,并且您也应该释放它(如果不维护对象的引用,这是不可能的).

所以,正确的方法是:

var workbooks = excel.Workbooks;
var workbook = workbooks.Open(/*params*/)

//business logic here

Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);
Run Code Online (Sandbox Code Playgroud)

  • `简单规则:避免使用双点调用表达式,例如这个` - 这已被[证明是错误的](/sf/answers/1759497981/)。 (2认同)

qJa*_*ake 20

这是我写的一段代码,因为我遇到了和你一样的问题.基本上,您需要关闭工作簿,退出应用程序,然后释放所有COM对象(而不仅仅是Excel应用程序对象).最后,调用垃圾收集器以获得良好的测量.

    /// <summary>
    /// Disposes the current <see cref="ExcelGraph" /> object and cleans up any resources.
    /// </summary>
    public void Dispose()
    {
        // Cleanup
        xWorkbook.Close(false);
        xApp.Quit();

        // Manual disposal because of COM
        while (Marshal.ReleaseComObject(xApp) != 0) { }
        while (Marshal.ReleaseComObject(xWorkbook) != 0) { }
        while (Marshal.ReleaseComObject(xWorksheets) != 0) { }
        while (Marshal.ReleaseComObject(xWorksheet) != 0) { }
        while (Marshal.ReleaseComObject(xCharts) != 0) { }
        while (Marshal.ReleaseComObject(xMyChart) != 0) { }
        while (Marshal.ReleaseComObject(xGraph) != 0) { }
        while (Marshal.ReleaseComObject(xSeriesColl) != 0) { }
        while (Marshal.ReleaseComObject(xSeries) != 0) { }
        xApp = null;
        xWorkbook = null;
        xWorksheets = null;
        xWorksheet = null;
        xCharts = null;
        xMyChart = null;
        xGraph = null;
        xSeriesColl = null;
        xSeries = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
Run Code Online (Sandbox Code Playgroud)

  • 真好 还值得注意的是,我还必须在循环中使用的Excel Range对象上调用ReleaseComObject。 (3认同)

Law*_*man 9

规则 - 永远不要再使用那一个点

- 一个点

var range = ((Range)xlWorksheet.Cells[rowIndex, setColumn]);
var hyperLinks = range.Hyperlinks;
hyperLinks.Add(range, data);
Run Code Online (Sandbox Code Playgroud)

- 两个或多个点

 (Range)xlWorksheet.Cells[rowIndex, setColumn]).Hyperlinks.Add(range, data);
Run Code Online (Sandbox Code Playgroud)

- 例子

 using Microsoft.Office.Interop.Excel;

 Application xls = null;
 Workbooks workBooks = null;
 Workbook workBook = null;
 Sheets sheets = null;
 Worksheet workSheet1 = null;
 Worksheet workSheet2 = null;

 workBooks = xls.Workbooks;
 workBook = workBooks.Open(workSpaceFile);
 sheets = workBook.Worksheets;
 workSheet1 = (Worksheet)sheets[1];


// removing from Memory
 if (xls != null)
 {    
   foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in sheets)
   {
      ReleaseObject(sheet);
   }

   ReleaseObject(sheets);
   workBook.Close();
   ReleaseObject(workBook);
   ReleaseObject(workBooks);

   xls.Application.Quit(); // THIS IS WHAT IS CAUSES EXCEL TO CLOSE
   xls.Quit();
   ReleaseObject(xls);

   sheets = null;
   workBook = null;
   workBooks = null;
   xls = null;

   GC.Collect();
   GC.WaitForPendingFinalizers();
   GC.Collect();
   GC.WaitForPendingFinalizers();
}
Run Code Online (Sandbox Code Playgroud)


flo*_*dis 9

摆脱所有引用是很棘手的,因为你必须猜测是否有如下调用:

var workbook = excel.Workbooks.Open("")
Run Code Online (Sandbox Code Playgroud)

创建一个Workbooks您没有引用的实例.

甚至参考如下:

targetRange.Columns.AutoFit()
Run Code Online (Sandbox Code Playgroud)

.Columns()在您不知道并且未正确发布的情况下创建实例.

我最后写了一个包含对象引用列表的类,它可以按相反的顺序处理所有对象.

Add()当您使用返回对象本身的Excel互操作时,该类具有您引用的任何对象和函数的列表:

    public List<Object> _interopObjectList = new List<Object>();

    public Excel.Application add(Excel.Application obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Range add(Excel.Range obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Workbook add(Excel.Workbook obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheet add(Excel.Worksheet obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheets add(Excel.Worksheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Sheets add(Excel.Sheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }


    public Excel.Workbooks add(Excel.Workbooks obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }
Run Code Online (Sandbox Code Playgroud)

然后取消注册对象我使用了以下代码:

    //Release all registered interop objects in reverse order
    public void unregister()
    {
        //Loop object list in reverse order and release Office object
        for (int i=_interopObjectList.Count-1; i>=0 ; i -= 1)
        { ReleaseComObject(_interopObjectList[i]); }

        //Clear object list
        _interopObjectList.Clear();
    }


    /// <summary>
    /// Release a com interop object 
    /// </summary>
    /// <param name="obj"></param>
     public static void ReleaseComObject(object obj)
     {
         if (obj != null && InteropServices.Marshal.IsComObject(obj))
             try
             {
                 InteropServices.Marshal.FinalReleaseComObject(obj);
             }
             catch { }
             finally
             {
                 obj = null;
             }

         GC.Collect();
         GC.WaitForPendingFinalizers();
         GC.Collect();
         GC.WaitForPendingFinalizers();
     }
Run Code Online (Sandbox Code Playgroud)

然后原则是创建类并捕获这样的引用:

//Create helper class
xlsHandler xlObj = new xlsHandler();

..

//Sample - Capture reference to excel application
Excel.Application _excelApp = xlObj.add(new Excel.Application());

..
//Sample - Call .Autofit() on a cell range and capture reference to .Columns() 
xlObj.add(_targetCell.Columns).AutoFit();

..

//Release all objects collected by helper class
xlObj.unregister();
Run Code Online (Sandbox Code Playgroud)

也许不是美丽的代码,但可能激发一些有用的东西.


JDB*_*JDB 7

在您的代码中,您有:

excel.Workbooks.Open(...)
Run Code Online (Sandbox Code Playgroud)

excel.Workbooks正在创建一个COM对象.然后,您Open将从该COM对象调用该函数.但是,在完成后,您不会释放COM对象.

处理COM对象时,这是一个常见问题.基本上,表达式中不应该有多个点,因为在完成后需要清理COM对象.

这个话题太大了,无法在答案中完全探索,但我认为你会发现Jake Ginnivan关于这个主题的文章非常有用:VSTO和COM Interop

如果您厌倦了所有这些ReleaseComObject调用,您可能会发现这个问题很有用:
如何在C#,2012版中正确清理Excel互操作对象


Hel*_*iac 5

正如其他答案中所述,使用两个点将创建无法由Marshal.FinalReleaseComObject. 我只是想分享我的解决方案,它消除了记忆的需要Marshal.FinalReleaseComObject——它真的很容易错过,而且很难找到罪魁祸首。

我使用了一个通用的 IDisposable 包装类,它可以用于任何 COM 对象。它的作用就像一个魅力,它让一切都保持干净整洁。我什至可以重用私有字段(例如this.worksheet)。由于 IDisposable 的性质(Dispose 方法作为 运行finally),它还会在出现错误时自动释放对象。

using Microsoft.Office.Interop.Excel;

public class ExcelService
{
    private _Worksheet worksheet;

    private class ComObject<TType> : IDisposable
    {
        public TType Instance { get; set; }

        public ComObject(TType instance)
        {
            this.Instance = instance;
        }

        public void Dispose()
        {
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(this.Instance);
        }
    }

    public void CreateExcelFile(string fullFilePath)
    {
        using (var comApplication = new ComObject<Application>(new Application()))
        {
            var excelInstance = comApplication.Instance;
            excelInstance.Visible = false;
            excelInstance.DisplayAlerts = false;

            try
            {
                using (var workbooks = new ComObject<Workbooks>(excelInstance.Workbooks))
                using (var workbook = new ComObject<_Workbook>(workbooks.Instance.Add()))
                using (var comSheets = new ComObject<Sheets>(workbook.Instance.Sheets))
                {
                    using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet1"]))
                    {
                        this.worksheet = comSheet.Instance;
                        this.worksheet.Name = "Action";
                        this.worksheet.Visible = XlSheetVisibility.xlSheetHidden;
                    }

                    using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet2"]))
                    {
                        this.worksheet = comSheet.Instance;
                        this.worksheet.Name = "Status";
                        this.worksheet.Visible = XlSheetVisibility.xlSheetHidden;
                    }

                    using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet3"]))
                    {
                        this.worksheet = comSheet.Instance;
                        this.worksheet.Name = "ItemPrices";
                        this.worksheet.Activate();

                        using (var comRange = new ComObject<Range>(this.worksheet.Range["A4"]))
                        using (var comWindow = new ComObject<Window>(excelInstance.ActiveWindow))
                        {
                            comRange.Instance.Select();
                            comWindow.Instance.FreezePanes = true;
                        }
                    }

                    if (this.fullFilePath != null)
                    {
                        var currentWorkbook = (workbook.Instance as _Workbook);
                        currentWorkbook.SaveAs(this.fullFilePath, XlFileFormat.xlWorkbookNormal);
                        currentWorkbook.Close(false);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message);
                throw;
            }
            finally
            {
                // Close Excel instance
                excelInstance.Quit();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)