当通过 OLE 嵌入文档时,Word 插件使 Word 进程保持活动状态

Word Add-in keeps Word process alive when document is embedded via OLE

我构建了一个 Word 加载项,它必须在用户关闭文档时执行一项任务。我为 Application 对象的 DocumentBeforeClose 事件安装了一个事件处理程序来执行此操作。问题是当我将现有的 Word 文档嵌入 Excel 工作簿时,事件处理程序似乎阻止 Word 进程退出。

当我通过功能区插入 > 对象 > 从文件创建然后 select Word 文档将文档嵌入 Excel 时出现问题。然后将文档加载到工作簿中。当我关闭 Excel 时,winword.exe 仍然是 运行 而 window 是不可见的。

当我禁用我的加载项时,winword.exe 只是 运行 大约一秒钟来嵌入文档。这也是我在启用加载项时想要的行为。

为了缩小问题范围,我创建了一个简单的 VSTO Word 插件用于测试。这些是我对 Visual Studio:

生成的 class 所做的更改
    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        Application.DocumentBeforeClose += Application_DocumentBeforeClose;
    }

    void Application_DocumentBeforeClose(Word.Document Doc, ref bool Cancel)
    {
        System.Windows.Forms.MessageBox.Show("Application_DocumentBeforeClose");
    }

    private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
    {
        System.Windows.Forms.MessageBox.Show("ThisAddIn_Shutdown");
    }

当我启动 Word 并再次关闭它时,此加载项会按预期显示两个消息框。当我在 Excel 工作簿中嵌入 Word 文档时,"DocumentBeforeClose" 消息会在一秒钟后弹出,但 "Shutdown" 消息不会出现。 winword.exe 即使在我关闭 Excel 后仍保留 运行。

删除连接事件处理程序的行会导致外接程序在嵌入文档 1 秒后显示 "Shutdown" 消息并 winword.exe 退出。

这让我想到事件处理程序导致对 Word 的应用程序对象的引用,从而阻止它关闭。所以我以相反的方式对其进行了测试,因为我还有一个 Excel 插件可以执行相同的任务。为了测试这一点,我构建了一个 Excel 加载项,就像上面的 Word 加载项一样,在 Word 文档中嵌入了一个工作簿,并且 Excel 加载项显示了两条消息,而没有留下 excel.exe进程。

为了找出问题的本质,我尝试解除 DocumentBeforeClose 中的事件处理程序:

        Application.DocumentBeforeClose -= Application_DocumentBeforeClose;
        GC.Collect();

这允许 winword.exe 关闭但也禁用我的加载项的功能。我不能使用该解决方案,因为如果只有一个文档并且用户关闭它但想继续使用 Word,也会发生该事件。加载项也必须继续工作。

我预计 Word 文档也将嵌入到其他第 3 方应用程序中,not embedding it 也不适合我。

至少在上面显示的 VSTO 2007 加载项和使用 PIA 的 Word 2013 加载项中会出现此问题。

我认为 weak event handlers 行不通,因为对 COM 对象的弱引用是不可能的,对吗?

我能否阻止事件处理程序使 Word 进程保持活动状态?有没有其他方法可以在 .NET 中挂接 DocumentBeforeClose 事件或 COM 事件?

编辑: 我也能够在 Word 2003 中用 Visual Basic 6 编写的加载项重现该问题。所以它似乎甚至不是 .NET相关。

与其在应用程序启动时附加 DocumentBeforeClose 事件处理程序,不如考虑在文档打开时附加它——并且仅当它不是嵌入对象时。

棘手的部分是确定正在打开的文档是否是嵌入对象。

我还没有找到任何记录的方法来明确检查自动化应用程序当前是否处于嵌入式模式,但是您可以检查很多东西来推断它是。

例如,我在下面突出显示了 Word 应用程序对象的一些属性,当打开的文档是嵌入对象时,这些属性不可用。如果您想检查它们是否可用,您可能需要使用异常处理,因为尝试访问此类属性可能无效。

您可以检查的另一件事是,在 WindowActivate 事件中,文档对象在作为嵌入对象打开时具有不同的 Kind 属性 值。 here 描述了一个嵌入在 Outlook 电子邮件中的文档的示例。