Adding custom data to Outlook e-mails

This article explains possible ways to attach custom information to Outlook emails. Let’s say you are writing a managed Outlook add-in and you would like to be able to add your own data to individual emails and then work with the data.

1. User defined fields

The first approach you can take is very simple. Every Outlook email has fields (like “From”, “Subject” and many others) and you can also add your own fields, like this:
public static void SetUserProperty(this MailItem mail, string value)
{
    mail.UserProperties.Add("myKey", OlUserPropertyType.olText,
        true, OlFormatText.olFormatTextText);
    mail.UserProperties["myKey"].Value = value;
}
The third (true) parameter addToFolderFields specifies whether this property should be defined just on the email or also on the folder in which the email resides. Defining the property also on the folder is necessary if you want this property to be available in user defined views. Now, we would like to be able to read the value of our property from the email. This is how to do it:
public static string GetUserProperty(this MailItem mail)
{
    if (mail.UserProperties["myKey"] == null)
        return null;
    return (string)mail.UserProperties["myKey"].Value;
}
Of course you can use other data type than string, or serialize an object to string. So far so good. The bad thing with User properties is that their value can get lost. When the MailItem is in a folder on IMAP account and it is moved to another folder, the value gets lost. On Exchange and POP3 accounts, the value should survive the move. In general Outlook’s support for some functionality is limited on IMAP compared to Exchange or POP3. When testing some functionality, always be sure to also test it on IMAP. The other bad thing is that when you realize you would also like to search all emails with certain value of your property, it turns out that it is quite slow. More on this in my next blogpost.

2. External database

This is the approach we are actually using in TaskConnect. It sounds quite simple as well:
  • For an email, store its unique ID and the extra data in a database.
  • To set the data, update the record having the email’s ID.
  • To get the data, find the record with the email’s ID.
It seems that the problem is solved. Every item in Outlook should have a unique identifier, right? Indeed it has, it is the EntryId. But unfortunately EntryId changes when the email is moved to different folder (on IMAP and even Exchange). It seems that there is no single unique permanent identifier of an email in Outlook’s database… Luckily there is one. It is the standard Message-ID, specified by RFC 2822. Although this RFC uses the word SHOULD, in practice it is always present. MessageId can be accessed from OOM like this:
public static string GetMessageId(this MailItem mailItem)
{
    var propertyAccessor = mailItem.PropertyAccessor;
    return (string)propertyAccessor.GetProperty(
        "http://schemas.microsoft.com/mapi/proptag/0x1035001E");
}
0x1035001E is a proptag, i.e. numeric identifier of “MessageId” field. To explore all the email fields and their proptags, see tools for viewing Outlook internals. Now the problem of adding custom data to an email seems solved, with 2 “subtle” issues:
  • In Outlook 2007, the MessageId is not present for outgoing emails in the Sent folder
  • If you realize that you would also like to find all emails having certain value of your custom data, it will be slow. More on this (and solution) in my next blogpost.

Other types of data than emails

Note that the approaches with UserProperties and EntryId described will also work for Contacts, Appointments, and all the other types of Outlook items. Unlike emails, some of them could be actually suitable for the UserProperties approach (because they are not stored on IMAP server for example).

Tools for viewing Outlook “internals”

Emails and everything else that Outlook works with is stored in so called MAPI store. MAPI is a COM API to work with this data basically. The managed Outlook Object Model (OOM) which you are using by referencing Microsoft.Office.Interop.Outlook is a wrapper around this COM API and actually it provides only the subset of all available functionality. If you need some functionality which is missing in OOM, try Redemption, which is a more complete wrapper around the COM API. The tools to view the “raw” underlying data in the MAPI store are OutlookSpy, and MFCMAPI. Sometimes they can prove very useful. OutlookSpy has the advantage that it is an Outlook add-in, while MFCMAPI is free and provides more functionality. This screenshot shows all the fields of an email. We can see our “myKey” field there. This is an email on POP3 account. Now we get to the reason for lost value on IMAP account: on IMAP the value is not stored among the fields of the email – it is stored somewhere else separately so it does not move with the email. Note that this is not necessarily Microsoft’s fault but it can be design decision caused by limitations of the IMAP protocol.