防止Excel退出

时间:2010-09-16 06:46:57

标签: c# excel com interop excel-2007

我错过了Excel.Application.QuitExcel.Application.BeforeQuit事件。 有人知道模仿这些事件的解决方法吗?

我通过COM Interop从C#WinForms应用程序访问Excel。给定一个Excel.Application对象,我该怎么做:

  1. 最好是防止Excel退出吗?
  2. 如果无法做到这一点,那么退出Excel时我至少可以注意
  3. 请注意:由于我有Excel.Application的COM引用,当Excel为Excel时,Excel进程退出用户“退出”。虽然这听起来很矛盾,但事实就是如此。通过“退出”我的意思是用户点击窗口右上角的“退出”或“十字按钮”。窗口关闭,文件被卸载,加载项被卸载以及Excel除了我没有任何线索之外做的任何东西。但是我仍然可以使用Application对象来“恢复”进程并再次使Excel可见,虽然这些加载项随后丢失,而且我还不知道还有什么处于未定义状态。

    要摆脱这个问题,我想在一开始就取消退出(想想BeforeQuit Cancel = true是否存在),或者至少在Excel退出时收到通知,所以我可以释放COM对象并使进程真正退出,下次我再次需要Excel时,我会知道我需要先启动它。

    不幸的是,这是一个恶性循环:只要Excel运行,我需要COM对象。因此,在 Excel退出之前,我无法处理它们。另一方面,只要COM对象存在,即使Excel假装退出,进程也不会退出,因此我不能等待进程退出事件或类似事件。

    我有一种令人不快的感觉,就是我要把头撞在砖墙上......

6 个答案:

答案 0 :(得分:7)

请注意,我没试过这个。

BeforeClose上创建一个包含代码的工作簿 例如

Option Explicit

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    Cancel = True
End Sub

打开此工作簿以及您拥有的其他工作簿&它不必被隐藏(如果整个应用程序不可见)。

因此,如果您尝试退出excel实例,它将强制关闭此隐藏的工作簿,这将提升其BeforeClose事件&你可以编写代码来阻止它关闭。

请注意,上面的代码是VB6(VBA),需要转换为c# 发表评论,如果您发现转换有任何困难。

如果要隐藏工作簿,可以执行

Workbooks("my workbook").Windows(1).Visible = False 

注意:工作簿有一个Windows集合。上面的代码试图隐藏第一个窗口 我不知道,一本工作簿可以有多个窗口吗?如果是这样,怎么样?

答案 1 :(得分:5)

在C ++中有一篇知识库文章How to automate Excel and then know the user closed it。我没有把它移植到C#,但它可能不是很多工作。

答案 2 :(得分:3)

当然这是一个黑客,但你不能使用WH_SHELL或WH_CBT的Windows SetWindowsHookEx API至少得到Excel主窗口被销毁的通知吗?

注意:它确实具有安全隐患,即需要一些管理员权限才能进行跨进程魔术。

答案 3 :(得分:3)

您要在此处尝试解决的问题不会通过监视程序退出来解决。 在你说我没有回答你的问题之前,你在问题中说明即使在用户退出excel之后你也能恢复excel。因此,excel.exe进程仍在使用中,因为您有一个.net对象,其中包含对excel.application的com互操作。

所以你有三个选择:

  1. 避免用户退出Excel。 正如你所说的那样让Excel退出,但是我不知道你可以阻止用户导致Excel退出的方式,因为你已经正确地注意到你正在卸载你的和其他任何插件,等等。请记住,微软专门设计通过这种方式进行用户交互,他们希望用户能够关闭他们的应用程序。你的插件需要能够处理这个,如果它不能我说你的插件有问题而不是Excel。我可能错了,因为我对你的应用程序要求不够了解。

  2. 在用户退出之前清除所有非托管资源。 在用户手动退出Excel之前,您需要做的是清理对alll Excel和Office非托管资源的引用,这样当它们退出时,您的应用程序代码不会留下任何现在指向不再使用excel实例的剩余资源已加载插件等。步骤(a)应该在您不再需要特定资源时立即执行,或者甚至在重新使用其他内容时(即Excel.Range类型)执行,而步骤(b)应该不经常使用,但是如果它一个胜利的应用程序,而不是一个插件,可能更频繁,这一切都取决于你的应用程序和你有机会的窗口(时间)之前用户可能完成任务关闭。显然,使用插件,您可以将其置于关闭事件中,或者在代码中进行仲裁。

    一个。如Otaku所述,在每个非托管资源上使用Marshal.FinalReleaseCOMObject,使用后为!= null。

        if (ComObject != null)
        {
            Marshal.FinalReleaseComObject(ComObject);
            ComObject = null;
        }
    

    湾将GC清理模式用于COM资源。

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    
  3. 重新加载插件 如果由于该任务的复杂性,时间限制(虽然我建议它),您不想完全跟踪和卸载所有非托管资源,您可以查看重新加载您在环境中可能已经知道的任何所需的插件。这仅在您控制环境时有效。 有手动加载Excel和COM插件的技术。至于其他的东西,我不知道这个,但是如果你在启动/ XLSTART目录中使用XLL或者XLT可能也可能,但是无论如何都会加载。

答案 4 :(得分:1)

为什么不执行System.Diagnostics.Process.Start(@"SomeWorkbook.xlsx");以确保启动Excel。如果它已经启动,那么这将不会创建新进程。

答案 5 :(得分:-1)

为什么不直接使用Application.ApplicationExit事件来了解它何时关闭?