Outlook.MailItem.PropertyChange stops firing after 9 events

244 Views Asked by At

I'm working on an Outlook Add-In that manages a project list based on emails that my production manager sends me. I have the project working well in VBA within outlook, but I'm tired of seeing the security message that pops up when using VBA. So, I'm converting it to a .Net Add-In. I have implemented this to a point in c#, but I'm running into an issue with an event listener for the Outlook.MailItem.PropertyChange event. The idea is that I can use the Quick Click Category to move the email into a Queue Folder, then run some processing on that email after it has been moved.

I'm subscribing to the event handler in the Startup method with the code below...

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        // Get the MAPI namespace
        _olNameSpace = this.Application.GetNamespace("MAPI");

        // Get the Application object
        _olApplication = this.Application;

        // Get the active Explorer object
        _olExplorer = _olApplication.ActiveExplorer();
        if (_olExplorer != null)
        {
            //Inbox Items and event listner(s)
            _olInBxItems = _olApplication.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items;
            foreach (object Item in _olInBxItems)
            {
                if (Item is Outlook.MailItem)
                {
                    _olMailItem = (Outlook.MailItem)Item;
                    _olMailItem.PropertyChange -= new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);
                    _olMailItem.PropertyChange += new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);
                }
            }
        }
    }

The issue I'm having is that the event triggers perfectly for about 9 emails, then it stops working for all but the first email in the Explorer. For the life of me, I can't figure out what is disposing of the event trigger for the rest of the emails.

I'm using the following definitions at the Class Level...

    internal static Outlook.NameSpace _olNameSpace { get; set; };
    internal static Outlook.Application _olApplication { get; set; }
    internal static Outlook.Items _olInBxItems { get; set; }
    internal static Outlook.MailItem _olMailItem { get; set; }
    internal static Outlook.Explorer _olExplorer { get; set; }

And this is the definition of my PropertyChange event handler...

    private static void Property_Change(string Name)
    {
        try
        {
            object curSelect = _olExplorer.Selection[1];
            if (((Outlook.MailItem)curSelect).Categories == "Post WIP")
            {
                MessageBox.Show(string.Format("{0} has changed in the {1} email.", Name, ((Outlook.MailItem)curSelect).Subject));
            }
            ((Outlook.MailItem)curSelect).Save();
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

What am I missing? Thanks in advance for looking.

3

There are 3 best solutions below

1
Patrick On

When setting the event handler, removing it is unnecessary before adding:

_olMailItem.PropertyChange -= new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);
_olMailItem.PropertyChange += new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);

You should be able to just add instead like this for each mail item:

_olMailItem.PropertyChange += new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);
0
CorruptedFile On

I believe I have fixed my issue. If I'm not mistaken it was a matter of falling out of scope. I am now building a list containing the email items in the explorer with the subscription to the event. I can now use the Quick Click category more than 9 times without the trigger failing. These are the changes I made to get this to work (***)...

internal static Outlook.NameSpace _olNameSpace { get; set; };
internal static Outlook.Application _olApplication { get; set; }
internal static Outlook.Items _olInBxItems { get; set; }
internal static Outlook.MailItem _olMailItem { get; set; }
internal static Outlook.Explorer _olExplorer { get; set; }

//*** Added this list to keep the emails in scope
internal static List<Outlook.MailItem> _olMIList = new List<Outlook.MailItem>();

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    // Get the MAPI namespace
    _olNameSpace = this.Application.GetNamespace("MAPI");

    // Get the Application object
    _olApplication = this.Application;

    // Get the active Explorer object
    _olExplorer = _olApplication.ActiveExplorer();
    if (_olExplorer != null)
    {
        //Inbox Items and event listner(s)
        _olInBxItems = _olApplication.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items;
        foreach (object Item in _olInBxItems)
        {
            if (Item is Outlook.MailItem)
            {
                //*** Changed this bit of code to use the list instead of the static _olMailItem
                ((Outlook.MailItem)Item).PropertyChange -= new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);
                ((Outlook.MailItem)Item).PropertyChange += new Outlook.ItemEvents_10_PropertyChangeEventHandler(Property_Change);
                _olMIList.Add((Outlook.MailItem)Item);
            }
        }
    }
}

I know I don't need to unsubscribe the event handler before it is subscribed, but it doesn't hurt anything if I do, and tends to work better in case the handler is already subscribed.

Any comments are welcome, I'm still learning c# and .net programming.

5
Dmitry Streblechenko On

Setting an event handler on each and every item in the Inbox folder is a horrible idea. Do not do that. It will fail spectacularly at the worst possible moment. It is guaranteed to fail in the online mode if you have more than a couple hundred items in the folder.

It is OK to trap the Explorer.SelectionChange event and only set an event handler on the selected items (from the Explorer.Selection collection) as long as the you immediately release the reference to the old items using Marshal.ReleaseComObject.