Question

I wrote an add-in for Outlook years ago that adds entries to a database based on the Item's ConversationIndex/ConversationId properties. This works great and remains uniform across all clients interacting with the messages (e.g. "Bob" can see that "Mary" already processed this message because an entry with the ConversationIndex already exists).

I'm now trying to move this piece to a service (and connect via the EWS API) but I'm not having good luck matching these properties with the values coming from Outlook. For example:

The Outlook Add-In will give me the following values for a specific email I'm targeting:

ConversationID:     6B6369F5023EA646AA7BC161274BDAE8
ConversationIndex:  0101CF3C7EEC6B6369F5023EA646AA7BC161274BDAE8

However, from the EWS API I get the following:

ConversationID:     AAQkADFhZThkNmJmLTlkODItNDQyZS1hM2YxLTQ2NWNkMTllYjhjOQAQAGtjafUCPqZGqnvBYSdL2ug=
ConversationIndex:  new byte[]{1,1,207,60,126,236,107,99,105,245,2,62,166,70,170,123,193,97,39,75,218,232}

I recognize the first as a Base64 encoded string, however what I get decoded doesn't look like anything I recognize (or can decipher). Is there anyone familiar with this, or who can help to get these two values to align? I can only imagine these properties come from the exchange server is some fashion, but the Client probably performs some cleansing whereas the EWS API just gives me the raw value (less the Base64 for what I presume transport purposes given the XML medium).

If anyone is familiar with this or has done it before I would greatly appreciate any guidance.

Side Note:

There are probably better ways to identify emails but for now I'm stuck with trying to keep these two synonymous. Modifying the outlook add-in isn't really an option, and once I migrate a 1:1 translation to the server (and drop the add-in) I'll have flexibility changing how it work. But for now I need them to run side-by-side. I need to be able to see processes made within Outlook from the web server and vise-versa.

Was it helpful?

Solution

Just found out (I think).

Breakdown

With more Googling and a bit more effort I believe I was able to make them align 1:1 using the following:

ConversationId

This is apparently an assembled value made up of several properties. Luckily I was able to find a method Woodman posted re-implementing the original algorithm used by Outlook here. With some minor modifications (to work with EWS instead of Outlook) I was able to get it to work.

ConversationIndex

This turned out to simply be a matter of using the BitConverter (and removing the hyphens). Easy peasy.

Final Result:

public static class EwsEmailMessageExtensions
{
    private const int c_ulConvIndexIDOffset = 6;
    private const int c_ulConvIndexIDLength = 16;
    private static ExtendedPropertyDefinition PidTagConversationIndexTracking = new ExtendedPropertyDefinition(0x3016, MapiPropertyType.Boolean);

    // HUGE props to Woodman
    // https://stackoverflow.com/a/21625224/298053
    public static string GetOutlookConversationId(this EmailMessage emailMessage)
    {
        Boolean convTracking;
        if (!emailMessage.TryGetProperty(PidTagConversationIndexTracking, out convTracking))
        {
            convTracking = true;
        }

        var convIndex = emailMessage.ConversationIndex;
        byte[] idBytes;
        if (convTracking && convIndex != null && convIndex.Length > 0)
        {
            // get Id from Conversation index
            idBytes = new byte[c_ulConvIndexIDLength];
            Array.Copy(convIndex, c_ulConvIndexIDOffset, idBytes, 0, c_ulConvIndexIDLength);
        }
        else
        {
            // get Id from Conversation topic
            var topic = emailMessage.ConversationTopic;
            if (string.IsNullOrEmpty(topic))
            {
                return string.Empty;
            }

            if (topic.Length >= 265)
            {
                topic = topic.Substring(0, 256);
            }
            topic = topic.ToUpper();

            using (var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider())
            {
                idBytes = md5.ComputeHash(Encoding.Unicode.GetBytes(topic));
            }
        }

        return BitConverter.ToString(idBytes).Replace("-", string.Empty);
    }

    public static String GetOutlookConversationIndex(this EmailMessage emailMessage)
    {
        var convIndex = emailMessage.ConversationIndex;
        return BitConverter.ToString(convIndex).Replace("-", String.Empty);
    }
}

Usage:

// Prep
ExchangeService service = new ExchangeService(...);
Folder inbox = Folder.bind(service, WellKnownFolderName.Inbox);
Item item = /* inbox.FindItems(...).First() */

// Implmentation
EmailMessage emailMessage = item as EmailMessage;
if (emailMessage != null)
{
   String conversationId = emailMessage.GetOutlookConversationId();
   String conversationIndex = emailMessage.GetOutlookConversationIndex();
   /* ... */
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top