c# - 在 C# Word Interop 中调用命令 Document.BeforeSave

标签 c# ms-word interop ms-office vsto

在我使用 VSTO 创建的 C# 互操作插件中,我订阅了 Document.BeforeSave事件。然而,另一个 MS Word 插件在我们客户的计算机上处​​于事件状态,也订阅了这个完全相同的事件。

第三方插件取消默认的 Word SaveAsDialog 并显示其自己的自定义 SaveAsDialog(它是一个 DMS 对话框)。 我们的用例是我们想要显示我们自己的 SaveAsDialog 并覆盖第三方的行为。

Document.BeforeSave 事件的调用顺序似乎是任意的。有时先调用我们的订阅者,有时先调用第三方插件。

有没有办法可靠地取消第三方调用?

编辑:

我试过下面的代码:

private void ThisAddIn_Startup(object sender, System.EventArgs e) {
    Application.DocumentOpen += Application_DocumentOpen;
}

void Application_DocumentOpen(Word.Document Doc) {
    Application.DocumentBeforeSave += Application_DocumentBeforeSave;
    var handler = new Word.ApplicationEvents2_DocumentBeforeSaveEventHandler(Application_DocumentBeforeSave);
    MulticastDelegate multicastDelegate = handler;
    var subscribers = handler.GetInvocationList();
    for (int i = 0; i < handler.GetInvocationList().Count(); i++) {
        Delegate.RemoveAll(multicastDelegate, subscribers[i]);
    }
    Application.DocumentBeforeSave += Application_DocumentBeforeSave2;
    Application.DocumentBeforeSave += Application_DocumentBeforeSave;
}

void Application_DocumentBeforeSave(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel) {
    MessageBox.Show("Save 1");
}

void Application_DocumentBeforeSave2(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel) {
    MessageBox.Show("Save 2");
}

这并没有达到让 2 个消息框连续显示“2”然后显示“1”的预期效果。相反,它显示“1”、“2”、“1”。

编辑 2:此代码按预期工作:

public class HasEvents {
    public delegate void WoeiHandler();
    public event WoeiHandler Woei;

    public void OnWoei() {
        Woei();
    }
}

public class Program {

    static void Main(string[] args) {
        HasEvents hasEvents = new HasEvents();
        hasEvents.Woei += () => Console.WriteLine("ShortVersion");
        hasEvents.Woei += Program_Woei;
        hasEvents.OnWoei();
        BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
        FieldInfo field = hasEvents.GetType().GetField("Woei", bindingFlags);
        MulticastDelegate multicastDelegate = (MulticastDelegate)field.GetValue(hasEvents);
        Delegate[] subscribers = multicastDelegate.GetInvocationList();

        Delegate current = multicastDelegate;
        for (int i = 0; i < subscribers.Length; i++) {
            current = Delegate.RemoveAll(current, subscribers[i]);
        }
        Delegate[] newSubscriptions = new Delegate[subscribers.Length + 1];
        newSubscriptions[0] = new HasEvents.WoeiHandler(Program_Woei_First);
        Array.Copy(subscribers, 0, newSubscriptions, 1, subscribers.Length);
        current = Delegate.Combine(newSubscriptions);
        field.SetValue(hasEvents, current);
        hasEvents.OnWoei();
    }

    static void Program_Woei() {
        Console.WriteLine("Program_Woei");
    }

    static void Program_Woei_First() {
        Console.WriteLine("First!");
    }

}

最佳答案

更新:It does not seem possible as per Jon Skeet (除了可能在 WPF 中,但肯定不在 Winforms 或加载项中)

我看到它完成的唯一方法是更改​​其他加载项的 LoadBehavior 注册表项。

另一个实验是尝试实现 System.ComponentModel.Component 并遵循此 answer .


免责声明,我还没有尝试过,但是看到我对另一个加载项重置我的菜单的评论意味着两个加载项都可以访问应用程序的相同实例我想如果事件签名相同,那么使用调用列表取消订阅应该有效。试一试这种方法,如果我有时间设置环境,我会尝试对此进行测试。

这是我对 Excel 的一次尝试:

Microsoft.Office.Tools.Excel.Workbook vstoDoc = Globals.Factory.GetVstoObject(this.Application.ActiveWorkbook);
WorkbookEvents_BeforeSaveEventHandler handler = vstoDoc.BeforeSave;

for (int i = 0; i < handler.GetInvocationList().Length; i++)
{
  try
  {
  vstoDoc.BeforeSave -= new Microsoft.Office.Tools.Excel.SaveEventHandler(ThisDocument_BeforeSave);
  }
catch { } //may raise exception if a handler is not attached.
}

Word 比较棘手(再次编译但我没有测试它, here is where I got the idea from 我不确定它是否有效):

var handler = new ApplicationEvents2_DocumentBeforeSaveEventHandler(Target); 

for (int i = 0; i < handler.GetInvocationList().Length; i++)
{
    try
    {
        vstoDoc.GetVstoObject().BeforeSave -= new Microsoft.Office.Tools.Word.SaveEventHandler((o, args) => { });
    }
    catch { } //may raise exception if a handler is not attached.
}

}

private void Target(Document doc, ref bool saveAsUi, ref bool cancel)
{
throw new NotImplementedException();
}

编辑:我很想知道为什么同样的 Office 对象模型在 word 中不起作用,但在 Excel 中编译:

Microsoft.Office.Tools.Word.Document vstoDoc = Globals.Factory.GetVstoObject(this.Application.ActiveDocument);
ApplicationEvents2_DocumentBeforeSaveEventHandler handler = vstoDoc.BeforeSave;
for (int i = 0; i < handler.GetInvocationList().Length; i++)
{
    try
    {
        vstoDoc.BeforeSave -= new Microsoft.Office.Tools.Word.SaveEventHandler(ThisDocument_BeforeSave);
    }
    catch
    {
    } //may raise exception if a handler is not attached.
}

无论如何,想法/Hack/等等是,一旦您取消订阅所有其他加载项以收听 BeforeSave 事件,那么您就可以分配您的加载项的 BeforeSave 事件,因此它是唯一触发的事件。

注意:您需要以 .Net 4.0 为目标,因为 Globals 中的 Factory。Factory 在 3.5 中不可用。

关于c# - 在 C# Word Interop 中调用命令 Document.BeforeSave,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26586026/

相关文章:

c# - 在 Visual Studio 的工具箱上应用过滤器?

c# - 逐行读取word文档

java - 使用 Apache POI Java NetBeans Word 文档的 CTPageSZ 类中的错误

C# P/调用 : How to achieve double indirection for a field of a structured parameter

c# - 如何使用 C 程序中的现有 C# 代码

C# 在使用 LoadLibrary/GetProcAddress 时调用 C 函数崩溃

c# - 如何在 VS 2015 中自定义 ASP.NET MVC 5 脚手架?

c# - 在应用程序中使用静态引用信息最 "testable"的方式是什么?

php - Microsoft Word 自动化 - 以编程方式删除标题(及其子 indo)

c# - 如何在 XAML 中动态绑定(bind)图像?