2014-10-27 69 views
3

在我使用VSTO創建的C#互操作插件中,我訂閱了Document.BeforeSave事件。但是,另一個MS Word插件在我們的客戶端計算機上也處於活動狀態,該訂閱者也訂閱了完全相同的事件。調用訂單Document.Before保存在C#Word Interop

第三方插件取消默認的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!"); 
    } 

} 
+0

+1我經歷了衝突VSTO加載項的痛苦。在一個案例http://stackoverflow.com/questions/10528775/how-to-add-a-menu-item-to-excel-2010-cell-context-menu-old-code-doesnt-work另一個加載項正在刪除我的菜單!無論如何,您可以將其他加載項事件處理程序取消訂閱到「Doc​​ument.BeforeSave」?值得谷歌... – 2014-11-01 07:37:03

+0

'Delegate.RemoveAll(multicastDelegate,訂戶[i]);'允許你重寫(即刪除)來自第三方插件的行爲嗎? – 2014-11-05 22:35:42

+0

我已經使用標準的.NET/C#反射(Delegate.RemoveAll())刪除了第一個事件處理程序。因此,不應該附加和呼叫這個。 – Ruudjah 2014-11-06 08:46:59

回答

0

UPDATE:It does not seem possible as per Jon Skeet(可能除了在WPF,但肯定不是在的WinForms或加載項)

我看到它的唯一方法正在完成的是通過更改其他AddIn的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. 
} 

詞是棘手的(這同樣編譯,但我沒有測試它,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對象模型不字工作,但在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. 
} 

反正思想/哈克的/ etc是,一旦你有退訂所有其他加載項聽BeforeSave事件,那麼你可以指定你的AddIn的BeforeSave事件所以它是唯一一個火災。

注意:您需要將.Net 4.0作爲Globals.Factory中的Factory在3.5中不可用。

+0

有可能看到我更新的問題,還要注意喬恩斯基特的預假設:「至少,不會與反思」 – Ruudjah 2014-11-06 13:59:06