Search code examples
c#outlookvstooutlook-addinoffice-addins

Outlook ItemAdd event registered from VSTO plugin and WPF app fires twice


I have two .NET applications: A VSTO plugin for Outlook and a WPF application. Both applications send e-mail with Outlook and need to do something to these sent e-mail items. To that end, both applications use Outlook interop and register the same method to the ItemAdd event for the sent mail folder.

Some users only use the VSTO; other users only use the WPF application. However, some users have the VSTO and also use the WPF application, and in that case, the method for the ItemAdd event is fired twice. I tried to get around this using a Singleton pattern (see below), however it is not working.

Please advise on how I can make it work, such that the HandleSentMail method is only fired once, even if the user has both the VSTO and is running the WPF application.

Registering event

Both the VSTO and the WPF application use the following code to attach a method HandleSentMail to the ItemAdd event (in the VSTO, it is placed in the Ribbon1_Load method):

OutlookInterface.Instance.SentMailItems.ItemAdd -= new ItemsEvents_ItemAddEventHandler(MailHandler.Instance.HandleSentMail);
OutlookInterface.Instance.SentMailItems.ItemAdd += new ItemsEvents_ItemAddEventHandler(MailHandler.Instance.HandleSentMail);

OutlookInterface.cs

public sealed class OutlookInterface {
  /// <summary>
  /// Singleton pattern, see
  /// https://csharpindepth.com/articles/singleton
  /// </summary>
  private static readonly Lazy<OutlookInterface> lazy = new Lazy<OutlookInterface>(() => new OutlookInterface());

  public static OutlookInterface Instance { get { return lazy.Value; } }

  public Application MailApplication { get; set; }

  public Items SentMailItems { get; set; }

  private OutlookInterface() {
    MailApplication = new Application();
    SentMailItems = MailApplication.Session.GetDefaultFolder(OlDefaultFolders.olFolderSentMail).Items;
  }
}

MailHandler.cs

public sealed class MailHandler {
  /// <summary>
  /// Singleton pattern, see
  /// https://csharpindepth.com/articles/singleton
  /// </summary>
  private static readonly Lazy<WorkZoneMailHandler> lazy = new Lazy<WorkZoneMailHandler>(() => new WorkZoneMailHandler());

  public static WorkZoneMailHandler Instance {
    get {
      return lazy.Value;
    }
  }

  public async void HandleSentMail(object item) {
    ...
  }
}

Solution

  • You can either have some kind of globally accessible db/list of processed messages and store the entry ids of the messages one of the two apps processed so that the other won't pick it up, or, better yet, give priority to one app over another (e.g. addin takes priority over the standalone app). You can use a named mutex for that in the HandleSentMail method - addin can open the mutex on startup (and close it on shutdown), but the standalone app can check if the mutex has already been opened, and if it was, skip processing:

    Mutex m = new Mutex(true, "YourCustomMutex", out bool createdNew);
    if (createdNew)
    {
        // The application is not already running.
    }