草庐IT

c# 和 excel 自动化 - 结束正在运行的实例

coder 2024-05-24 原文

我正在通过 C# 尝试 Excel 自动化。我已按照 Microsoft 提供的所有说明进行操作,但我仍在努力放弃对 Excel 的最终引用,以使其关闭并使 GC 能够收集它。

代码示例如下。当我注释掉包含类似于以下行的代码块时:

Sheet.Cells[iRowCount, 1] = data["fullname"].ToString();

然后文件保存并退出 Excel。否则文件会保存,但 Excel 会作为一个进程继续运行。下次运行此代码时,它会创建一个新实例,并且它们最终会建立起来。

任何帮助表示赞赏。谢谢。

这是我的代码的准系统:
        Excel.Application xl = null;
        Excel._Workbook wBook = null;
        Excel._Worksheet wSheet = null;
        Excel.Range range = null;

        object m_objOpt = System.Reflection.Missing.Value;

        try
        {
            // open the template
            xl = new Excel.Application();
            wBook = (Excel._Workbook)xl.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate, false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
            wSheet = (Excel._Worksheet)wBook.ActiveSheet;

            int iRowCount = 2;

            // enumerate and drop the values straight into the Excel file
            while (data.Read())
            {

                wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();
                wSheet.Cells[iRowCount, 2] = data["brand"].ToString();
                wSheet.Cells[iRowCount, 3] = data["agency"].ToString();
                wSheet.Cells[iRowCount, 4] = data["advertiser"].ToString();
                wSheet.Cells[iRowCount, 5] = data["product"].ToString();
                wSheet.Cells[iRowCount, 6] = data["comment"].ToString();
                wSheet.Cells[iRowCount, 7] = data["brief"].ToString();
                wSheet.Cells[iRowCount, 8] = data["responseDate"].ToString();
                wSheet.Cells[iRowCount, 9] = data["share"].ToString();
                wSheet.Cells[iRowCount, 10] = data["status"].ToString();
                wSheet.Cells[iRowCount, 11] = data["startDate"].ToString();
                wSheet.Cells[iRowCount, 12] = data["value"].ToString();

                iRowCount++;
            }

            DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
            _report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
            wBook.Close(true, _report.ReportLocation, m_objOpt);
            wBook = null;

        }
        catch (Exception ex)
        {
            LogException.HandleException(ex);
        }
        finally
        {
            NAR(wSheet);
            if (wBook != null)
                wBook.Close(false, m_objOpt, m_objOpt);
            NAR(wBook);
            xl.Quit();
            NAR(xl);
            GC.Collect();
        }

private void NAR(object o)
{
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
    }
    catch { }
    finally
    {
        o = null;
    }
}

更新

无论我尝试什么,“干净”方法或“丑陋”方法(请参阅下面的答案),只要命中这一行,excel 实例仍然会挂起:
wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();

如果我注释掉该行(显然还有其他类似的行),Excel 应用程序会正常退出。只要上面的一行被取消注释,Excel 就会继续存在。

我想我将不得不在分配 xl 变量之前检查是否有一个正在运行的实例,然后将其 Hook 。我忘了提到这是一个 Windows 服务,但这应该无关紧要,是吗?

最佳答案

更新 (2016 年 11 月)

我刚刚读了一篇 convincing argument由 Hans Passant 使用 GC.Collect实际上是正确的方法。我不再使用 Office(谢天谢地),但如果我这样做了,我可能想再试一次 - 它肯定会简化我编写的许多(数千行)代码,试图做“正确的事情” “方式(正如我当时所看到的那样)。

我会把我原来的答案留给后人...

正如迈克在他的回答中所说,有一种简单的方法可以解决这个问题。 Mike 建议使用简单的方法,因为……它更容易。我个人不认为这是一个足够好的理由,我也不认为这是正确的方法。对我来说,它有“关掉再打开”的感觉。

我有几年在 .NET 中开发 Office 自动化应用程序的经验,这些 COM 互操作问题在我第一次遇到这个问题的前几周和几个月里一直困扰着我,尤其是因为 Microsoft 非常腼腆地承认存在问题首先,当时在网上很难找到好的建议。

我有一种工作方式,我现在几乎不假思索地使用它,而且我已经好几年没遇到问题了。对您可能正在创建的所有隐藏对象保持活跃仍然很重要 - 是的,如果您错过了一个,您可能会发现泄漏,直到很久以后才会变得明显。但这并不比以前的情况更糟malloc/free .

我确实认为在你走的时候清理你自己,而不是在最后。如果您只是开始使用 Excel 来填充几个单元格,那么也许这无关紧要 - 但如果您要进行一些繁重的工作,那就是另一回事了。

无论如何,我使用的技术是使用实现 IDisposable 的包装类。 ,并在其 Dispose 中方法调用 ReleaseComObject .这样我就可以使用 using语句以确保在我完成后立即处理对象(并释放 COM 对象)。

至关重要的是,即使我的函数提前返回,或者出现异常等,它也会被释放/释放。此外,如果它实际上是首先创建的,它只会被释放/释放 - 称我为学究但建议试图释放实际上可能没有创建的对象的代码在我看来就像草率的代码。我对使用 FinalReleaseComObject 也有类似的反对意见——您应该知道创建 COM 引用的次数,因此应该能够释放相同的次数。

我的代码的典型片段可能如下所示(或者,如果我使用的是 C# v2 并且可以使用泛型 :-),则可能是这样):

using (ComWrapper<Excel.Application> application = new ComWrapper<Excel.Application>(new Excel.Application()))
{
  try
  {
    using (ComWrapper<Excel.Workbooks> workbooks = new ComWrapper<Excel.Workbooks>(application.ComObject.Workbooks))
    {
      using (ComWrapper<Excel.Workbook> workbook = new ComWrapper<Excel.Workbook>(workbooks.ComObject.Open(...)))
      {
        using (ComWrapper<Excel.Worksheet> worksheet = new ComWrapper<Excel.Worksheet>(workbook.ComObject.ActiveSheet))
        {
          FillTheWorksheet(worksheet);
        }
        // Close the workbook here (see edit 2 below)
      }
    }
  }
  finally
  {
    application.ComObject.Quit();
  }
}

现在,我不想假装这不是冗长的,如果不将内容分成更小的方法,由对象创建引起的缩进可能会失控。这个例子是最坏的情况,因为我们所做的只是创建对象。通常,大括号之间发生的事情更多,开销要少得多。

请注意,根据上面的示例,我将始终在方法之间传递“包装”对象,而不是裸 COM 对象,并且处理它是调用者的责任(通常使用 using 语句)。类似地,我总是会返回一个包装好的对象,而不是一个裸露的对象,再次释放它是调用者的责任。你可以使用不同的协议(protocol),但有明确的规则很重要,就像我们过去必须做我们自己的内存管理一样。
ComWrapper<T>希望这里使用的类几乎不需要解释。它只是存储对包装的 COM 对象的引用,并在其 ReleaseComObject 中显式释放它(使用 Dispose )。方法。 ComObject方法简单地返回对包装的 COM 对象的类型化引用。

希望这可以帮助!

编辑 :我现在才按照链接转到 Mike's answer to another question ,并且我看到该问题的另一个答案有一个指向包装类的链接,就像我上面建议的那样。

此外,关于迈克对另一个问题的回答,我不得不说我几乎被“只使用 GC.Collect”的论点所吸引。然而,我主要是在一个错误的前提下被吸引住了。乍一看,它似乎根本不需要担心 COM 引用。但是,正如 Mike 所说,您仍然需要显式释放与所有范围内变量关联的 COM 对象 - 因此您所做的只是减少而不是消除对 COM 对象管理的需要。就个人而言,我宁愿全力以赴。

我还注意到在编写代码的许多答案中,所有内容都在方法结束时被释放,在一个大块 ReleaseComObject 中的趋势。调用。如果一切都按计划进行,那一切都很好,但我会敦促任何编写严肃代码的人考虑如果抛出异常,或者如果该方法有多个退出点(代码不会被执行,因此 COM 对象不会被释放)。这就是为什么我喜欢使用“包装器”和 using s。它很罗嗦,但它确实有助于防弹代码。

编辑2 :我已经更新了上面的代码,以指示应该在保存或不保存更改的情况下关闭工作簿的位置。这是保存更改的代码:
object saveChanges = Excel.XlSaveAction.xlSaveChanges;

workbook.ComObject.Close(saveChanges, Type.Missing, Type.Missing);

...并且不保存更改,只需更改 xlSaveChangesxlDoNotSaveChanges .

关于c# 和 excel 自动化 - 结束正在运行的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1041266/

有关c# 和 excel 自动化 - 结束正在运行的实例的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  3. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  4. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  5. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  6. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  7. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  8. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  9. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  10. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

随机推荐