【问题标题】:Why is killing the Excel process a bad thing?为什么杀死 Excel 进程是一件坏事?
【发布时间】:2013-07-22 07:03:21
【问题描述】:

我看过很多关于如何确保 Excel 在您想要的时候真正退出并且进程不会继续存在的文章和问题。 Here is a knowledge Base article 描述问题和微软推荐的解决方案。本质上:

'close files

'Quit Excel
xlApp.quit()

'Release and collect garbage
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlApp)
GC.Collect()
GC.WaitForPendingFinalizers()

很多人不建议杀掉进程;看 How to properly clean up Excel interop objectsUnderstanding Garbage Collection in .net

另一方面,很多人不推荐使用 GC.Collect。见What's so wrong about using GC.Collect()?

根据我的经验,终止进程是确保 Excel 消失的最快和最简单的方法。我的代码只杀死它启动的确切进程,没有其他。我确保关闭所有打开的工作簿,退出应用程序并释放 xlApp 对象。最后我检查进程是否还活着,如果还活着,就杀死它。

<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
ByRef lpdwProcessId As Integer) As Integer
    End Function

Sub testKill()

    'start the application
    Dim xlApp As Object = CreateObject("Excel.Application")

    'do some work with Excel

    'close any open files

    'get the window handle
    Dim xlHWND As Integer = xlApp.hwnd

    'this will have the process ID after call to GetWindowThreadProcessId
    Dim ProcIdXL As Integer = 0

    'get the process ID
    GetWindowThreadProcessId(xlHWND, ProcIdXL)

    'get the process
    Dim xproc As Process = Process.GetProcessById(ProcIdXL)

    'Quit Excel
    xlApp.quit()

    'Release
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlApp)

    'set to nothing
    xlApp = Nothing

    'kill the process if still running
    If Not xproc.HasExited Then
        xproc.Kill()
    End If

End Sub

我看到很多人说杀死进程是不好的,但我还没有看到任何关于原因的定性答案。特别是在确保文件已关闭之后,Excel 已退出,我们只会终止我们开始的确切进程。我的问题是杀死 Excel 进程的潜在问题是什么。会影响性能吗?它会损害 Excel 吗?

许多人还会说,通过良好的编码,我不必终止进程。也许吧,但这并不能回答“为什么终止进程不好?”的问题。关闭文件后,退出 Excel 并释放对象;为什么绝对确定该过程已经消失会是一件坏事?

编辑:Excel 退出后还剩下什么?如果 Excel 可见,它似乎正常退出,从视图和任务栏中消失。 Excel实际上退出了还是没有退出。在我看来,Excel 实际上确实退出了,而且我们只有一个空的进程 shell 正在运行。任何人都可以对此发表评论吗?

编辑:有趣的是我注意到 GC(又名垃圾收集)通过 GC.Collect() GC.WaitForPendingFinalizers() 实际上会释放 Excel 退出后留下的进程外壳。这是否支持我的假设,即空进程外壳毕竟真的是垃圾?

编辑:刚刚找到一个关于这个问题的优秀网站:50 Ways to kill Excel

【问题讨论】:

  • 杀死一个进程就像从墙上猛拉电源线来关闭您的计算机。当然,它有效,但它不是关闭程序的正确方法。
  • @CodyGray 现代机器的设计目的是从电源线抽出中恢复得相当好。如果你还没有保存它,你只会丢失一些东西。我猜他想知道现代 Excel 是否同样适用?
  • @Toby 我不同意。是的,已经做了一些工作来防止事情像过去那样出错,但这并不意味着它是关闭系统的有效方法。安全工程师在汽车上工作,以确保您在发生事故时也不会死亡,但这并不意味着您不需要安全驾驶。所以是的,Windows 通常会在进程终止后进行清理,但这并不是正确的做法。
  • @DavidColwell 我已经编写了许多使用 Excel 执行某些重要工作的应用程序,并且在 Excel 中执行某些操作后遇到了许多 Excel 进程未关闭的问题。
  • 我发现摆脱 excel.exe 需要三件事:将对 excel 应用程序的所有引用设置为 null/nothing、GC.Collect()应用程序必须具有被用户或通过调用Quit 关闭,如果用户取消了不计入的关闭。当我做这一切时,我只会在调试时得到额外的 excel.exes(因为停止执行不会清理。)

标签: c# .net vb.net excel excel-interop


【解决方案1】:

看,事实是,如果可能,您应该始终让应用正常退出。杀死应用程序是最后的手段。我了解您确信在这种情况下您认为这样做没有任何问题,也许您是正确的。即使杀死它对您的系统绝对没有负面影响,但这并不能改变这样做是错误的事实。这就像违法,因为您知道自己不会被抓到,而且无论如何这是一种没有受害者的犯罪。

但是,您可能没有意识到潜在的副作用。例如,当您强制终止应用程序时,操作系统可能会为其保留崩溃数据。或者它可能会向 Microsoft 发送崩溃遥测数据。您实际上是在告诉 Microsoft,应用程序崩溃的频率比实际情况要高,导致其崩溃统计数据略有偏差。

另一个可能的副作用是注册表配置单元可能无法正确卸载。您可能不时在事件日志中看到此错误。这通常发生在应用程序被强制关闭并且没有正确关闭注册表句柄时。

即使这些事情都没有发生,您也无法知道操作系统的未来版本可能会做什么。今天行得通的,明天可能行不通。这就是为什么您应该始终遵循记录在案的 API 和指南,因为他们通常会非常努力地支持他们已发布的内容,但通常不会非常努力地支持他们明确告知的内容你不要这样做。

【讨论】:

  • 是的,只要软件开发人员继续做这样的事情,用户总会想知道为什么他们的机器的性能和稳定性似乎随着时间的推移而下降,他们不得不重新安装操作系统。跨度>
  • 这里有点主观,但杀死Excel并不是“违法”。您必须这样做,否则当您的系统上运行 20 个或更多“不可见”的 Excel 进程时,您将不得不重新启动计算机。这是 Excel 的错,而不是用户的错。终止 Excel 不会导致发送遥测数据,并且如果终止进程,您不会遇到注册表配置单元卸载问题。操作系统将确保在进程被终止时关闭所有打开的注册表句柄,从而允许在注销时卸载配置单元。此外,这是 Office 问题,而不是操作系统问题。
  • 您确定这不是程序员的问题,不是所有对现有对象的打开引用都已被删除,以便允许应用程序正常退出吗?
  • Lasse 正在做某事。代码确实 尝试以正常方式退出 Excel,调用 quit 方法。然后它检查它是否已经退出,如果没有,就杀死它。该 if 语句中还应该有一个断言,并且需要进行一些调试以找出 为什么 Excel 还没有像您要求的那样退出。
  • @CodyGray 实际上我们知道为什么 Excel shell 进程不退出。微软有帮助地指出,非托管 COM 引用被计算在内,一旦它们全部被释放,Excel 就应该退出。然而,在花了几个小时调试和计算引用之后,我得出结论,这只是浪费时间。所以现在的问题是为什么在 Excel 已经退出后杀死空进程 shell 是不好的。我邀请您提出自己的答案,如果他们喜欢您的答案,请让其他用户投票给您。我已经知道你不喜欢我的回答,但没关系。您的意见很有帮助。
【解决方案2】:

当您使用自动化从另一个应用程序控制 Office 应用程序时,您有时必须像您一样终止 Office 进程以避免“泄漏”不可见的 Office 应用程序。这是 Office 应用程序试图既充当最终用户应用程序又充当自动化服务器的不幸结果。

在服务器端使用 Word 时,我采用了与您大致相同的解决方案(不要问为什么)。无论我们为正确关闭 Word 付出了多少努力,服务器上都会积累越来越多的“不可见”Word 进程。唯一好的解决方案是杀死那些在被指示退出后不会终止的进程。

当一个 Windows 进程被杀死时,该进程使用的所有资源都会被操作系统清理掉,例如文件和其他操作系统句柄(如注册表句柄)已关闭、内存已释放等。从操作系统的角度来看,当进程终止时不会泄漏任何内容。

但是,应用程序可能会创建要在正常关机期间删除的临时文件。随着时间的推移,这些孤立文件可能会使用越来越多的磁盘空间。此外,如果用户在应用程序中打开了其他文件,则这些文件可能会在进程终止时处于不一致的状态,并且可能会丢失未保存的更改。基本上,被杀死的应用程序可以“泄漏”的是它打算在关闭时清理或删除的文件。 “泄漏”的另一个来源是在网络上获取的资源(例如,在共享上打开的文件)。但是,当进程终止时,网络句柄最终会变得“陈旧”并被网络服务器回收。

另外,我想指出,如果进程被终止,Watson 博士将不会收集任何故障转储数据。这只发生在进程意外崩溃(例如,有未处理的异常)时。

底线:如果你小心杀死 Excel 可能是避免随着时间的推移“泄漏”不可见的 Excel 进程的最佳方法。在系统重新启动之前让它们使用越来越多的系统资源运行的替代方法是不可行的。如果有的话,成本应该只是临时文件夹中留下的一些小文件。


作为自动化 Office 的替代方法,您可以使用 Open XML SDK 打开和修改 Office 文件。最初它可能会稍微复杂一些,但您可以完全避免在此过程中启动繁重的 Office 应用程序。

【讨论】:

  • 您是否有一些官方文档的链接可以保证“从操作系统的角度来看,当进程终止时不会泄露任何内容”?是的,操作系统会尝试在您之后进行清理,但这只是一个安全网,可以捕获开发人员可能遗漏的内容。你不应该指望这种行为。我想从操作系统的角度来看,一切都是笨拙的,但你正在杀死的应用程序可能不会分享这个观点。
  • @CodyGray:任何带有进程的适当操作系统都会在进程终止时清理进程使用的所有资源。这是操作系统的基本承诺。如果有任何东西“泄露”,它会发生在内核中(即驱动程序或操作系统错误),因为进程完全消失了。
  • 请求文档链接以保证操作系统的基本行为有点像请求保证在汽车中踩下制动踏板会停止汽车。在印刷品中找到它可能并不那么容易。不过,我可以推荐阅读Operating Systems Design and ImplementationWindows Internals 之类的书籍。
  • “任何适当的操作系统” - 是非 squiter。首先,所有操作系统都有错误,Windows 以即使在杀死进程后也会出现资源泄漏错误而闻名。 Kernel32 和 GDI32 经常泄漏永远不会被清理的句柄,这是因为出于性能原因这些是共享进程,并且这些系统中的错误会导致共享资源出现问题。其次,我没有说 Watson 博士,Windows 收集了大量与 Watson 无关的遥测数据。
【解决方案3】:

根据我的经验,程序在关闭时会做一些事情:

  1. 释放所有内存引用
  2. 删除任何临时文件或工作文件
  3. 保存任何州数据

出于这些原因,关键以 API 描述的方式使用 app.Quit() 关闭 Excel。此示例与规范的不同之处在于 API 并未释放所有 COM 对象。这导致步骤 1) 不完整。无法确保应用程序在所有情况下都有效关闭,因为您可能无法始终控制创建的 COM 对象。

我发现使用 Excel(以及其他办公程序)的最佳方法是使用以下过程:

  1. 获取名称包含 Excel 的进程 ID 列表
  2. 打开 Excel
  3. 重复第 1 步。使用它来确定您创建的新 Excel 实例的进程 ID
  4. 使用 Excel
  5. 退出 Excel,释放您创建的所有对象并以 Application.Quit() 终止
  6. 杀死进程

这使 Excel 有机会在进程终止之前释放任何对象、删除任何临时文件并保存任何状态数据。我通常创建一个负责管理 excel 实例的单例类。它实现了 IDisposable,在 Disposal 时,它将退出所有剩余的 Excel 应用程序。

【讨论】:

  • 我建议您使用 App.HWND > GetWindowThreadProcessId(xlHWND, ProcIdXL) > Process.GetProcessById(ProcIdXL) 而不是第 1 步和第 3 步(请参阅上面的问题了解我的操作方法)
  • Excel 退出时也不会删除临时文件并保存状态数据吗?如果是这样,这个过程中真正剩下的是什么?我怀疑留下的Excel进程实际上只是一个根本不包含Excel的空壳。它只是需要删除的剩余内存指针。
  • 你是对的,我的论点的要点是你应该调用 Quit 函数(这样 Excel 就可以完成所有的清理工作),剩下的就是一个附有一些随机 COM 对象的空壳,因此杀死空壳是可以的。在相关说明中,为什么使用 app.HWND?它更可靠吗?
  • App.HWND 为您提供正确的窗口句柄,您可以从中获取进程 ID。这样,您就可以确信您拥有正确的流程,而无需担心整个流程 ID 列表。它避免了在您获得第二个列表之前在启动 Excel 的确切时刻启动第二个 Excel 实例的不太可能的情况。使用 App.HWND 更可靠。
  • 当然我完全同意使用 App.Quit 退出 Excel。
猜你喜欢
  • 2012-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
相关资源
最近更新 更多