在visual studio中关闭Microsoft Excel工作表c#

时间:2017-02-26 06:21:14

标签: c# wpf excel

我正在设计一个WPF应用程序,我打开一个excel文件,然后在其中写入一些数据然后关闭它。现在我还需要发送电子邮件到我需要附加此excel文件的电子邮件地址。但是在附加它时抛出了一个异常,即该文件被另一个进程使用。我查看了任务管理器,发现Microsoft excel正在后台运行。

我寻找了一些可用的解决方案并编辑了如下代码:

        xlWorksheet.SaveAs("C:\\Data.xlsx");
        Marshal.FinalReleaseComObject(xlWorksheet);
        xlWorkbook.Close();
        Marshal.FinalReleaseComObject(xlWorkbook);
        xlApp.Quit();
        Marshal.FinalReleaseComObject(xlApp);

现在这似乎有效,因为它关闭了微软excel。但结束需要花费很多时间。有时它立即关闭excel但有时需要3-5分钟才能关闭excel。因为我需要在excel中保存数据后发送电子邮件,然后如何处理这种情况。在我的应用程序中,用户将按下按钮并执行以下任务:

  1. 打开Excel工作表
  2. 将数据保存到其中。
  3. 关闭Excel工作表
  4. 启动SMTP
  5. 添加电子邮件地址
  6. 附加excel表
  7. 发送电子邮件
  8. 现在在附件中,它失败了,因为excel在后台打开所以它显示错误。如何立即关闭excel表。有没有替代方案的解决方案。

2 个答案:

答案 0 :(得分:3)

使用Excel互操作时,您必须超级按钮。我不会重新发布SO上已有的所有内容,但这里是我的ExcelInterfacer类的介绍注释:

/*
General rules on working with interop COM objects boil down to "Never use 2 dots" (always hold a named
reference to all COM objects so that you can explicitely release them via Marshal.FinalReleaseComObject()): 
https://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects/1307180#1307180
Also, don't force the GC to cleanup in the same method where objects are used (or debugging breaks):
https://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net/17131389#17131389 
(We initiate our GC calls when disposing.)
*/

How do I properly clean up Excel interop objects?

Understanding garbage collection in .NET

在我的程序中,我只在必要时打开Excel,并且总是将ExcelInterfacer类包装在using块中。为此,你必须实现IDisposable,但这很容易。这样做,我的Excel进程在它们不再使用后很快就会关闭,但是你仍然是垃圾收集的奴隶 - 除非你立刻强制它,我猜。

在using()块之后的下一个代码语句中,我从未尝试过使用Excel文档。您可能想要检查要邮寄的隐藏的〜$版本的Excel文件是否仍然存在或已关闭。这将清楚地表明该文件是可访问的。

我的建议:

  1. 阅读上面的帖子并理解它们。将Excel RCW从创建的C#对象中分离出来很容易,然后你的FinalReleaseComObject()调用也不会做太多。
  2. 为所有Excel操作创建一个类,并确保它 正确实现IDisposable。
  3. 在using()块中通过该类包装所有Excel交互。
  4. 检查〜$文件是否已消失,作为Excel文件是否可以访问的指示。
  5. 最后:习惯于定期检查您的运行流程。如果您的程序在Excel互操作处于活动状态时崩溃(或者您退出调试会话),则可以孤立该Excel进程。习惯于手动删除它们,以便在启动下一个调试会话时不会运行Excel。
  6. 从那里开始:)作为参考,来自我的Excel界面类的IDisposable代码:

    // Properly clean up Excel on quitting. See:
    // https://stackoverflow.com/questions/2260990/com-object-that-has-been-separated-from-its-underlying-rcw-cannot-be-used
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    private void Dispose(bool disposing)
    {
        if (m_Disposed)
            return;
    
        // I know this looks like total overkill, but per http://www.xtremevbtalk.com/tutors-corner/160433-automating-office-programs-vb-net-com-interop.html           
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    
        if (disposing)
        {
            // Free managed objects
            CloseWorkbook();
    
            if (m_Application != null)
            {
                m_Application.Quit();
                Marshal.FinalReleaseComObject(m_Application);
            }
    
            m_Application = null;
        }
    
        m_Disposed = true;
    }
    
    ~ExcelInterfacer()
    {
        Dispose(false);
    }
    

答案 1 :(得分:1)

我想建议尝试使用EPPLUS?它将帮助您分离需要安装机器的EXCEL Api,以及具有更清晰(更清洁)的API(无编组/ com等...)来处理 - 换句话说,释放你的EXCEL INTEROP 以及它的 COM 复制的API(双关语)。

EPPLUS会很好地处理您的用例,因为无法打开Excel流程,因此在将文件附加到您的电子邮件时,这不再是一个问题因为excel进程不与文件绑定。您可以使用EPPLUS控制流程。

因此,除非您需要在Excel中进行非常具体的 API调用,但正如我看到您只是添加数据我觉得您不会需要非常具体的Excel功能 - 我感觉EPPLUS总是一个更好,更清洁的选择。

  1. 在您正在运行代码的机器上分离您需要EXCEL
  2. 无需担心混淆COM / Excel Interop API
  3. 内存效率更高,部分原因是它无法启动excel流程
  4. 支持 xlsx 以及以下内容: 单元格范围,单元样式(边框,颜色,填充,字体,数字,对齐),图表,数据透视表,表格,图片,形状,注释,保护,加密,数据验证,条件格式,VBA,公式计算,还有更多...

    检查https://www.nuget.org/packages/EPPlushttp://epplus.codeplex.com/documentation 以下是一些示例用法:

    using(var package = new ExcelPackage(new FileInfo(@"c:\temp\tmp.xlsx")))
    {
       // calculate all formulas in the workbook
       package.Workbook.Calculate();
       // calculate one worksheet
       package.Workbook.Worksheets["my sheet"].Calculate();
       // calculate a range
       package.Workbook.Worksheets["my sheet"].Cells["A1"].Calculate();
       // create a new sheet and write and manipulate cells on it
       var ws = package.Workbook.Worksheets.Add("new sheet");
       ws.Cells["B1"].Value = "some data";
       ws.Cells["C1"].Value = "some more data";
       ws.Cells["B1:E1"].Style.Font.Bold = true;
       // Save your EXCEL sheet, the using statement will close all references afterwards - since it EPPLUS neatly provides the IDisposable pattern around an excel file (aka ExcelPackage).
       package.Save();
    }