Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VSTO: process mail using newmailex before outlook rules move mail

Tags:

vsto

outlook

I am creating an addon for Outlook 2007 that reads a mail item when it is received, and then rewrites it. The addon works great, and rewrites the mail for items that do not have an Outlook rule that moves them into another folder. If there is a rule, it is still fine about 50% of the time. The other 50% of the time, the rule moves the mail item before my addon finishes. I get the following error:

"The operation cannot be performed because the object has been deleted."

I am using NewMailEx event to call my rewriting function:

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    this.Application.NewMailEx += new Outlook.ApplicationEvents_11_NewMailExEventHandler(olApp_NewMail);
}

In Outlook 2007, NewMailEx gives an entryID for the mail. This entryID is used initially to figure out which mail object to use:

Outlook.NameSpace outlookNS = this.Application.GetNamespace("MAPI");
Outlook.MAPIFolder mFolder = this.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.MailItem mail;
try
{
    mail = (Outlook.MailItem)outlookNS.GetItemFromID(entryIDCollection, Type.Missing);
}
catch (Exception e) { Debug.WriteLine("exception with non-mail item " + entryIDCollection + ": " + e.ToString()); return; }

I thought that I could take this entryID (which the above code works), and iterate through all of my folders (on exchange as well as on my computer) looking for the same mail id. When I finally iterate to where the mail is, the moved mail's EntryID is very different from the entryIDCollection.

Maybe I'm going about this the wrong way. Does anyone know how to stop the event from propagating until I am done, or how to track down the moved email?

Here is my code for traversing the folders in case anyone's curious:

        try
        {
            mail.Subject = new_subj;
            mail.Body = "";
            mail.HTMLBody = text;
            mail.ClearConversationIndex();
            mail.Save();
        }
        catch (Exception ex)
        {
            //It wasn't caught in time, so we need to find the mail:
            ArrayList unreadFolders = new ArrayList();
            foreach (Outlook.Folder f in outlookNS.Folders) unreadFolders.Add(f);

            while (unreadFolders.Count > 0)
            {
                Outlook.Folder currentFolder = unreadFolders[0] as Outlook.Folder;
                Debug.WriteLine("reading folder: " + currentFolder.Name);
                unreadFolders.RemoveAt(0);


                foreach (Outlook.Folder f in currentFolder.Folders) unreadFolders.Add(f);

                try
                { 
                    Outlook.Items items = currentFolder.Items.Restrict("[UnRead] = true");
                    for (int itemNum = 1; itemNum <= items.Count; itemNum++)
                    {
                        if (!(items[itemNum] is Outlook.MailItem)) continue;
                        Outlook.MailItem m = items[itemNum];
                        if (m.EntryID == entryIDCollection)
                        {
                            m.Subject = new_subj;
                            m.Body = "";
                            m.HTMLBody = text;

                            m.ClearConversationIndex();
                            m.Save();
                            return;
                        }

                    }
                }
                catch (Exception exc) { }
            }

        }
like image 660
mdiehl13 Avatar asked Feb 20 '10 11:02

mdiehl13


2 Answers

Untested Idea : If you are reliably getting the NewMailEx Event, mark the Mail with a user property or mileage with a GUID and then use Search for that.

This may not work as you may not be able to get in before the Rule moves the mail.

As you have worked out the EntryId changes whe the item is moved.

Other way you need to look at MAPI props to get the PR_SEARCH_KEY that dosent change when mail is moved around.

like image 138
76mel Avatar answered Oct 12 '22 13:10

76mel


76mel's answer worked great! I am posting my resulting code just in case others want to do something similar (I'm new and not sure about the rules of posting lots of code, so sorry if it's against the rules):

private string getPRSearchKey(Outlook.MailItem m)
{
    return m.PropertyAccessor.BinaryToString(m.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x300B0102"));
}

private void olApp_NewMail(string entryIDCollection)
{
    Outlook.NameSpace outlookNS = this.Application.GetNamespace("MAPI");
    Outlook.MAPIFolder mFolder = this.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
    Outlook.MailItem mail;

    string pr_search_key;
    string old_subj;
    string old_body;
    try
    {
        mail = (Outlook.MailItem)outlookNS.GetItemFromID(entryIDCollection, Type.Missing);
        pr_search_key = getPRSearchKey(mail);
        //save the pr_search_key, subject, and body before the mailItem gets moved
        // then we can work on it without worrying about them disappearing
        old_subj = mail.Subject;
        old_body = mail.Body;
    }
    catch (Exception e) { Debug.WriteLine("exception with non-mail item " + entryIDCollection + ": " + e.ToString()); return; }

    //
    // ... do stuff with the mail's body and subject
    //

    try
    {
        mail.Subject = new_subj;
        mail.Body = "";
        mail.HTMLBody = text;

        mail.ClearConversationIndex();
        mail.Save();
    }
    catch (Exception ex)
    {
        //It wasn't caught in time, so we need to find the mail:
        ArrayList unreadFolders = new ArrayList();
        foreach (Outlook.Folder f in outlookNS.Folders) unreadFolders.Add(f);

        while (unreadFolders.Count > 0)
        {
            Outlook.Folder currentFolder = unreadFolders[unreadFolders.Count-1] as Outlook.Folder;
            Debug.WriteLine("reading folder: " + currentFolder.Name);
            unreadFolders.RemoveAt(unreadFolders.Count - 1);


            foreach (Outlook.Folder f in currentFolder.Folders) unreadFolders.Add(f);

            try
            { 
                Outlook.Items items = currentFolder.Items.Restrict("[UnRead] = true");
                for (int itemNum = 1; itemNum <= items.Count; itemNum++)
                {
                    if (!(items[itemNum] is Outlook.MailItem)) continue;
                    Outlook.MailItem m = items[itemNum];
                    if (getPRSearchKey(m) == pr_search_key)
                    {
                        m.Subject = new_subj;
                        m.Body = "";
                        m.HTMLBody = text;

                        m.ClearConversationIndex(); //don't think this works
                        m.Save();
                        return;
                    }

                }
            }
            catch (Exception exc) { }
        }

    }
}

btw, something that I will probably change is I will skip querying certain folders to speed it up a bit (Journal, Deleted Items, Junk E-mail, Drafts, RSS Feeds, Microsoft at Home, Tasks, Notes, Contacts, Calendar, Sent Items, Outbox).

like image 27
mdiehl13 Avatar answered Oct 12 '22 14:10

mdiehl13