2016-04-15 11 views
1

これまでの間、この種の質問をするときに私が試したすべてを含めるように以前から尋​​ねられました。VSTO Word post save event

私はWordアドインを作成しており、Wordオブジェクトモデルを使用して達成できないドキュメントを変更する必要があります。したがって、ドキュメントがディスクに保存された後、そのイベントをキャプチャし、ファイルを閉じ、必要な処理を行い、再度開く必要があります。 (私は知っている、エレガントではないが、それは私が使用する必要があります)。

Wordには保存前と終了前がありますが、保存後はありません。他のスレッドを作成してCOMのIMessageFilter(System.Windows.Formsからではない)を使用してCOMリトライコールを処理するか、メインスレッドにメッセージをポストして保存した後にコードを実行することで、保存後のイベントをシミュレートすることができました。しかし、Word.Documentオブジェクトが既に削除されているため、ユーザーがドキュメントを閉じようとした結果ファイルが保存されると、 "コールバック"メソッドでファイル名を取得できないため、これは機能しません。

私はBeforeSaveイベントハンドラでSave自分自身を明示的に呼び出して、Cancel = trueを返しました。これは、ユーザーが保存を選択したときや、一度ディスクに保存したときに効果的です。しかし、ユーザーが保存せずに新しい文書を閉じて、保存するかどうかを「はい」を選択した場合、WordはBeforeSaveイベントから戻った後で既に保存を処理した後に別の「SaveAs」ダイアログを表示します。 BeforeSaveイベントハンドラでCancel = trueを設定します。

それで、BeforeCloseイベントと同様のことをやってみました。私はクローズを処理して自分自身を保存してから、イベントハンドラからCancel = trueを返します。しかし、そうすることで、ユーザがアプリケーションをシャットダウンしようとしているときに、複数のドキュメントを閉じようとするのを止める。

私はWM_CLOSEの処理も試みましたが、上記のような同様の問題が発生します。

誰でもソリューションを提供できますか?

答えて

2

私はthisを訪れましたが、あなたがしたいことをするかもしれないと思います。それが消えた場合のためにそこにあるもののコピーがここにあります。


私は私の最初のWord AfterSave Eventのエントリを書いたとき、それはWord 2007のために設計された、とあった - それは結局のところ - ではないキャッチすべて。だから私はここでそれを更新した(キャッチのためのおかげでPat Lemmに行く)。

文書を閉じると、保存されたファイル名にアクセスできなくなりました。初期化時に

  1. があなたのWordオブジェクトを渡す:ここ

    は、それがどのように動作するかであるので、私はここで、コードを更新している、それが今、すべての条件で動作し、Word 2013でテストされています。

  2. 保存前イベントにアタッチします。
  3. 保存イベントが発生すると、バックグラウンドでの保存が完了するまでループするスレッドが起動します。
  4. 保存背景が完了したら、それは本当==文書保存されたかどうかをチェックします:

    • 保存が== trueの場合:その後、定期的に保存が発生しました。
    • 保存が== falseの場合:それはそれぞれのケースで

自動保存しなければならなかった、それはユニークなイベント発生します:

  • をAfterSaveUiEvent
  • AfterSaveEvent
  • AfterAutoSaveEvent

また、保存されているドキュメントも閉じている場合は、途中でWindowDeactivateイベントのファイル名をキャッチします。これで、(下の例のように)呼び出し元がアクセスできるようになり、閉じたドキュメントの完全なファイル名を取得できます。ここで

は、クラスのコードです:

public class WordSaveHandler 
{ 
    public delegate void AfterSaveDelegate(Word.Document doc, bool isClosed); 
    // public events 
    public event AfterSaveDelegate AfterUiSaveEvent; 
    public event AfterSaveDelegate AfterAutoSaveEvent; 
    public event AfterSaveDelegate AfterSaveEvent; 
    // module level 
    private bool preserveBackgroundSave; 
    private Word.Application oWord; 
    string closedFilename = string.Empty; 

    /// <summary> 
    /// CONSTRUCTOR takes the Word application object to link to. 
    /// </summary> 
    /// <param name="oApp"></param> 
    public WordSaveHandler(Word.Application oApp) 
    { 
     oWord = oApp; 
     // hook to before save 
     oWord.DocumentBeforeSave += oWord_DocumentBeforeSave; 
     oWord.WindowDeactivate += oWord_WindowDeactivate; 
    } 

    /// <summary> 
    /// Public property to get the name of the file 
    /// that was closed and saved 
    /// </summary> 
    public string ClosedFilename 
    { 
     get 
     { 
      return closedFilename; 
     } 
    } 

    /// <summary> 
    /// WORD EVENT fires before a save event. 
    /// </summary> 
    /// <param name="Doc"></param> 
    /// <param name="SaveAsUI"></param> 
    /// <param name="Cancel"></param> 
    void oWord_DocumentBeforeSave(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel) 
    { 
     // This could mean one of four things: 
     // 1) we have the user clicking the save button 
     // 2) Another add-in or process firing a resular Document.Save() 
     // 3) A Save As from the user so the dialog came up 
     // 4) Or an Auto-Save event 
     // so, we will start off by first: 
     // 1) Grabbing the current background save flag. We want to force 
     // the save into the background so that Word will behave 
     // asyncronously. Typically, this feature is on by default, 
     // but we do not want to make any assumptions or this code 
     // will fail. 
     // 2) Next, we fire off a thread that will keep checking the 
     // BackgroundSaveStatus of Word. And when that flag is OFF 
     // no know we are AFTER the save event 
     preserveBackgroundSave = oWord.Options.BackgroundSave; 
     oWord.Options.BackgroundSave = true; 
     // kick off a thread and pass in the document object 
     bool UiSave = SaveAsUI; // have to do this because the bool from Word 
     // is passed to us as ByRef 
     new Thread(() => 
     { 
      Handle_WaitForAfterSave(Doc, UiSave); 
     }).Start(); 
    } 

    /// <summary> 
    /// This method is the thread call that waits for the same to compelte. 
    /// The way we detect the After Save event is to essentially enter into 
    /// a loop where we keep checking the background save status. If the 
    /// status changes we know the save is compelte and we finish up by 
    /// determineing which type of save it was: 
    /// 1) UI 
    /// 2) Regular 
    /// 3) AutoSave 
    /// </summary> 
    /// <param name="Doc"></param> 
    /// <param name="UiSave"></param> 
    private void Handle_WaitForAfterSave(Word.Document Doc, bool UiSave) 
    { 
     try 
     { 
      // we have a UI save, so we need to get stuck 
      // here until the user gets rid of the SaveAs dialog 
      if (UiSave) 
      { 
       while (isBusy()) 
        Thread.Sleep(1); 
      } 

      // check to see if still saving in the background 
      // we will hang here until this changes. 
      while (oWord.BackgroundSavingStatus > 0) 
       Thread.Sleep(1); 
     } 
     catch (ThreadAbortException) 
     { 
      // we will get a thread abort exception when Word 
      // is in the process of closing, so we will 
      // check to see if we were in a UI situation 
      // or not 
      if (UiSave) 
      { 
       AfterUiSaveEvent(null, true); 
      } 
      else 
      { 
       AfterSaveEvent(null, true); 
      } 
     } 
     catch 
     { 
      oWord.Options.BackgroundSave = preserveBackgroundSave; 
      return; // swallow the exception 
     } 

     try 
     { 
      // if it is a UI save, the Save As dialog was shown 
      // so we fire the after ui save event 
      if (UiSave) 
      { 
       // we need to check to see if the document is 
       // saved, because of the user clicked cancel 
       // we do not want to fire this event 
       try 
       { 
        if (Doc.Saved == true) 
        { 
         AfterUiSaveEvent(Doc, false); 
        } 
       } 
       catch 
       { 
        // DOC is null or invalid. This occurs because the doc 
        // was closed. So we return doc closed and null as the 
        // document 
        AfterUiSaveEvent(null, true); 
       } 
      } 
      else 
      { 
       // if the document is still dirty 
       // then we know an AutoSave happened 
       try 
       { 
        if (Doc.Saved == false) 
         AfterAutoSaveEvent(Doc, false); // fire autosave event 
        else 
         AfterSaveEvent(Doc, false); // fire regular save event 
       } 
       catch 
       { 
        // DOC is closed 
        AfterSaveEvent(null, true); 
       } 
      } 
     } 
     catch { } 
     finally 
     { 
      // reset and exit thread 
      oWord.Options.BackgroundSave = preserveBackgroundSave; 
     } 
    } 

    /// <summary> 
    /// WORD EVENT – Window Deactivate 
    /// Fires just before we close the document and it 
    /// is the last moment to get the filename 
    /// </summary> 
    /// <param name="Doc"></param> 
    /// <param name="Wn"></param> 
    void oWord_WindowDeactivate(Word.Document Doc, Word.Window Wn) 
    { 
     closedFilename = Doc.FullName; 
    } 

    /// <summary> 
    /// Determines if Word is busy essentially that the File Save 
    /// dialog is currently open 
    /// </summary> 
    /// <param name="oApp"></param> 
    /// <returns></returns> 
    private bool isBusy() 
    { 
     try 
     { 
      // if we try to access the application property while 
      // Word has a dialog open, we will fail 
      object o = oWord.ActiveDocument.Application; 
      return false; // not busy 
     } 
     catch 
     { 
      // so, Word is busy and we return true 
      return true; 
     } 
    } 
} 

そして、ここでは、あなたがそれを設定してに接続する方法です、それはイベントです:

public partial class ThisAddIn 
{ 
    WordSaveHandler wsh = null; 
    private void ThisAddIn_Startup(object sender, 
            System.EventArgs e) 
    { 
     // attach the save handler 
     wsh = new WordSaveHandler(Application); 
     wsh.AfterAutoSaveEvent += new WordSaveHandler.AfterSaveDelegate(wsh_AfterAutoSaveEvent); 
     wsh.AfterSaveEvent += new WordSaveHandler.AfterSaveDelegate(wsh_AfterSaveEvent); 
     wsh.AfterUiSaveEvent += new WordSaveHandler.AfterSaveDelegate(wsh_AfterUiSaveEvent); 
    } 
    void wsh_AfterUiSaveEvent(Word.Document doc, bool isClosed) 
    { 
     if (!isClosed) 
      MessageBox.Show("After SaveAs Event"); 
     else 
      MessageBox.Show("After Close and SaveAs Event. The filname was: " + wsh.ClosedFilename); 
    } 

    void wsh_AfterSaveEvent(Word.Document doc, bool isClosed) 
    { 
     if (!isClosed) 
      MessageBox.Show("After Save Event"); 
     else 
      MessageBox.Show("After Close and Save Event. The filname was: " + wsh.ClosedFilename); 
    } 

    void wsh_AfterAutoSaveEvent(Word.Document doc, bool isClosed) 
    { 
     MessageBox.Show("After AutoSave Event"); 
    } 

    // etc. 

}