【问题标题】:Excel 2007 Hangs When Closing via .NET通过 .NET 关闭时 Excel 2007 挂起
【发布时间】:2010-09-19 20:32:36
【问题描述】:

我有一个需要打开和关闭 Excel 电子表格的 Visual Basic .NET 程序。打开和阅读电子表格工作正常,但试图关闭 Excel 2007 应用程序会导致它挂起。它似乎关闭了,但是如果您查看任务管理器,该应用程序仍在运行。我用来关闭它的代码是

wbkData.Close(saveChanges:=False)
appExcel.Quit()
wbkData = Nothing
appExcel = Nothing

如何让 Excel 正确关闭?

【问题讨论】:

    标签: .net vb.net excel


    【解决方案1】:

    我认为这里已经涵盖了您的问题的答案: How to properly clean up excel interop objects in c

    我无法从您的代码示例中看到,但基本上,始终将您的 excel 对象分配给局部变量,永远不要“向下两个点”,如下所示:

    //FAIL
    
    Workbook wkBook = xlApp.Workbooks.Open(@"C:\mybook.xls");
    

    而是单独引用每个 obj:

    //WIN
    
    Worksheets sheets = xlApp.Worksheets;
    Worksheet sheet = sheets.Open(@"C:\mybook.xls");
    ...
    Marshal.ReleaseComObject(sheets);
    Marshal.ReleaseComObject(sheet);
    

    .NET 为您不可见的 COM 对象创建一个包装器,并且在 GC 编织其魔法之前不会释放。

    在我发现这一点之前,每次我创建一个新的工作簿来检查 excel.exe 进程的年龄并杀死任何超过一分钟的工作簿时,我都会在 ASP.NET 应用程序中运行下面的 hacky 代码:

    //force kill any excel processes over one minute old.
    try
    {
        Process[] procs = Process.GetProcessesByName("EXCEL");
        foreach (Process p in procs)
        {
            if (p.StartTime.AddMinutes(1) < DateTime.Now)
            {
                p.Kill(); 
            }  
        }  
    }
    catch (Exception)
    {}  
    

    【讨论】:

    • 感谢您指向另一个问题的指针。他们似乎确实解决了同样的问题。
    【解决方案2】:

    我给你写了那篇帖子mentioned on the Excel Team Blog...

    我之前也在 StackOverflow 上针对问题 How to properly clean up Excel interop objects in C# 讨论过这个问题。

    该问题的第一个答案被标记为“正确”并获得 11 票,但我向您保证,该政策在实践中极难正确使用。如果有人在任何地方滑倒并使用“两个点”,或者通过 for each 循环或任何其他类似的命令迭代单元格,那么您将拥有未引用的 COM 对象并冒着挂起的风险——而且没有办法在代码中查找原因。

    相反,您采用的清理程序绝对是可行的方法。

    【讨论】:

    • 嗨,迈克。感谢您在 Excel 团队博客上发布的非常有帮助的帖子。这是我在 Internet 上可以找到的有关此问题的唯一信息。它使我免于许多小时的挫败感。
    • 没问题,埃里克,我很高兴它有帮助! :-)
    【解决方案3】:

    我找到了一个对我有用的solution in the MSDN Excel Blog。是这样解释的

    上面有两个问题:

    (1) 虽然代码看起来 首先处理“wbkData”对象, 等等,上面的代码实际上并没有 强制执行,因为 .NET 垃圾收集程序可以处理 它的对象以任何顺序。 (GC是 非确定性的,不仅仅是 时间不确定。)

    (2) 'wsh = 等命令 wbkData.Workssheets.Item(1)' -- 或 像这样的线条——很常见而且 将创建一个包装一个 RCW 对象 “工作表”对象。你不会有 持有对它的引用的变量,所以 你一般不会考虑 但这个 RCW 对象不会 处理到下一个垃圾 收藏。但是,上面的代码 调用 GC.Collect() last,因此 RCW 仍然持有对 这个“工作表”对象何时 appExcel.Quit() 被调用。 Excel 挂起 结果。

    最终代码看起来像

    GC.Collect()
    GC.WaitForPendingFinalizers()
    
    wbkData.Close(SaveChanges:=False)
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wbkData) : wbkData = Nothing
    appExcel.Quit()
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(appExcel) : appExcel = Nothing
    

    【讨论】:

      猜你喜欢
      • 2020-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-07
      • 2014-09-22
      • 1970-01-01
      相关资源
      最近更新 更多