views:

672

answers:

2

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) { }
            }

        }
+3  A: 

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.

76mel
Great, that did it! I had it grab the PR_SEARCH_KEY right when the message came in. I also grabbed the body and subject. Then I modified the subject and body. When I try and update the body/subject, and it fails, I then have it search through the folders and find the matching mail. Although PR_SEARCH_KEY is not unique IF the mail item is copied (both copies may share same PR_SEARCH_KEY), that is perfectly fine, because when it comes in, I haven't made any copies yet.I tried to vote this up, but I'm too new to vote :( I'll post my code below (running out of comment characters)
mdiehl13
+1  A: 

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

mdiehl13
Also, sometimes when I get the newmailex the mail has already been moved (found this out later). I modified my first catch statement so that if pr_search_key == "", grab all unread mail items, and see if I have modified them yet... kind've annoying. Another issue I have seen is that when my computer comes out of sleep, any new emails received do not send a newmailex, and so all of those are missed. Very annoying
mdiehl13