VSTO Word post save event

Désolé pour la longueur de ceci, dans le passé on m’a demandé auparavant d’inclure tout ce que j’ai essayé en posant ce genre de questions.

J’écris un complément Word et je dois apporter des modifications au document que je ne peux pas utiliser à l’aide du modèle object Word. Par conséquent, une fois le document enregistré sur le disque, je dois capturer cet événement, fermer le fichier, faire ce que je dois faire et le rouvrir. (Je sais, pas élégant, mais c’est ce que je dois travailler avec.)

Word a Avant enregistrer et Avant fermer, mais pas après l’événement de sauvegarde. J’ai trouvé des astuces en ligne pour simuler un événement après enregistrement en créant un autre thread et en utilisant IMessageFilter de COM (non de System.Windows.Forms) pour gérer les appels de nouvelle tentative COM ou pour poster un message sur le thread principal afin que je puisse exécuter le code après son enregistrement. . Mais cela ne fonctionne pas, car si le fichier est enregistré à la suite d’une tentative de fermeture du document par l’utilisateur, je ne peux pas obtenir le nom du fichier dans ma méthode “callback” car l’object Word.Document a déjà été supprimé.

J’ai donc essayé d’appeler explicitement Enregistrer moi-même dans mon gestionnaire d’événements BeforeSave et renvoyer Cancel = true. Cela fonctionne très bien lorsque l’utilisateur sélectionne l’enregistrement ou s’il a déjà été enregistré sur le disque. Mais si l’utilisateur ferme un nouveau document sans enregistrer, puis sélectionne “oui” pour indiquer s’il souhaite ou non enregistrer, Word affiche une autre boîte de dialog “Enregistrer sous” après que j’ai déjà géré la sauvegarde après mon retour de l’événement BeforeSave, même si J’ai mis Cancel = true dans mon gestionnaire d’événements BeforeSave.

Alors j’ai essayé de faire quelque chose de similaire avec l’événement BeforeClose. Je gère la fermeture et me sauve moi-même, puis retourne Cancel = true à partir de mon gestionnaire d’événements. Cela empêche toutefois Word d’essayer de fermer plusieurs documents lorsque l’utilisateur tente de fermer l’application.

J’ai même essayé de gérer WM_CLOSE, mais cela posait les mêmes problèmes que ci-dessus.

Quelqu’un peut-il offrir une solution?

Je suis tombé sur cette idée il y a quelque temps, je pense que cela peut faire ce que vous voulez. Voici une copie de ce qui est là au cas où il disparaîtrait jamais.


Lorsque j’ai écrit ma première entrée dans l’ événement Word AfterSave , elle était conçue pour Word 2007 et n’était pas un piège. Donc, je l’ai mis à jour ici (merci pour la capture allez à Pat Lemm).

Lorsque le document a été fermé, vous n’avez jamais access au nom de fichier enregistré. Donc, j’ai mis à jour le code ici et il fonctionne maintenant dans toutes les conditions et a été testé dans Word 2013.

Voici comment cela fonctionne:

  1. Lors de l’initialisation, vous passez votre object Word.
  2. Il est attaché à l’événement Before Save.
  3. Lorsqu’un événement de sauvegarde se produit, il lance un fil de discussion qui rest en boucle jusqu’à la fin de la sauvegarde en arrière-plan.
  4. Une fois la sauvegarde en arrière-plan terminée, il vérifie si le document Saved == true:

    • Si Saved == true: une sauvegarde régulière a eu lieu.
    • Si enregistré == faux: alors il devait s’agir d’une sauvegarde automatique

Dans chaque cas, un événement unique sera déclenché:

  • AfterSaveUiEvent
  • AfterSaveEvent
  • AfterAutoSaveEvent

De plus, si le document en cours de sauvegarde est également en cours de fermeture, nous récupérons le nom de fichier dans l’événement WindowDeactivate lors de la sortie. L’appelant peut maintenant accéder à cette page (comme vous pouvez le voir dans l’exemple ci-dessous) pour obtenir le nom de fichier complet du document fermé.

Voici le code à la classe:

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; ssortingng closedFilename = ssortingng.Empty; ///  /// CONSTRUCTOR takes the Word application object to link to. ///  ///  public WordSaveHandler(Word.Application oApp) { oWord = oApp; // hook to before save oWord.DocumentBeforeSave += oWord_DocumentBeforeSave; oWord.WindowDeactivate += oWord_WindowDeactivate; } ///  /// Public property to get the name of the file /// that was closed and saved ///  public ssortingng ClosedFilename { get { return closedFilename; } } ///  /// WORD EVENT fires before a save event. ///  ///  ///  ///  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(); } ///  /// 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 ///  ///  ///  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; } } ///  /// WORD EVENT – Window Deactivate /// Fires just before we close the document and it /// is the last moment to get the filename ///  ///  ///  void oWord_WindowDeactivate(Word.Document Doc, Word.Window Wn) { closedFilename = Doc.FullName; } ///  /// Determines if Word is busy essentially that the File Save /// dialog is currently open ///  ///  ///  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; } } } 

Et voici comment vous le configurez et associez-le à ses événements:

 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. }