Calling order Document.BeforeSave in C # Word Interop

In my C # interop addin created using VSTO, I subscribe to the Document.BeforeSave event. However, another MS Word addin is active on our client computer, also subscribing to this same event.

The third party admin overrides the default Word SaveAsDialog and shows its own SaveAsDialog file (this is the DMS dialog ). Our use case is that we want to show our own SaveAsDialog and override the third person behavior.

The ordering of the Document.BeforeSave event seems arbitrary. Sometimes our caller is called first, sometimes the third party addin is called first.

Is there a way to reliably cancel a third party call?

Edit:

I've tried the following code:

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");
}

      

This does not have the intended effect of having two messages showing "2" and then "1" in sequence. Instead, it displays "1", "2", "1".

Edit 2: This code works as intended:

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!");
    }

}

      

+3


source to share


1 answer


UPDATE: This is not possible according to Jon Skeet (except maybe in WPF, but certainly not in Winforms or Add-Ins)

The only way I can see is to do this by changing the LoadBehavior registry key of another AddIn.

Another experiment is to try System.ComponentModel.Component

and follow this.


Disclaimer, I haven't tried this, but after seeing your comment about resetting my menu again, I added that the add-ins have access to the same instance of the app I'm drawing if the event signatures match, then unsubscribe using the calllist should work. Play around with this approach, I'll try to test it out if I have time to tweak the environment.

Here's what I did with 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.
}

      



The word was tricky (again this compiles, but I haven't tested it, this is where I got the idea from and I'm not sure if this would work):

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();
}

      

Edit: I'm pulling my hair out on why this same Office object model doesn't work by word, but compiles to 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.
}

      

Anyway, the idea behind / Hack / etc was that after you have unsubscribed from all other add-ons to listen to the BeforeSave event, you can assign the AddIn BeforeSave event so that it is the only one that fires.

Note: you need to target .Net 4.0 like Factory

Globals.Factory in 3.5.

0


source







All Articles