Set up properties of EmailMessage

I write client application that uses Exchange Web Services managed API v.2.0 in order to connect to Exchange Web Services. Sometimes, I need create EmailMessage object and make it looks like as received letter. Therefore I need set up such properties of EmailMessage as ReceiveBy, DateTimeCreate, DateTimeReceived, but they haven’t public set assessors. Using ILDasm utility we find out that values of these properties are stored in the inner dictionary PropertyBag. So the values of necessary properties can be set up by using reflection.
The code below shows this workaround. By the way this code doesn’t connect to Exchange Server, so ExchangeService object may have fake Url property.

*************************************************
using System;
using Microsoft.Exchange.WebServices.Data;

namespace Exchange.Mail
{
    public static class EmailMethods
    {
        public static EmailMessage CreateEmailMessage(ExchangeService service,
             string subject,
             string body,
             string address)
        {
            if (service == null)
                return null;

            var message = new EmailMessage(service)
                {
                    Subject = subject,
                    Body = body,
                    ItemClass = "IPM.Note",
                    From = null
                };
            SetProperty(message, EmailMessageSchema.ReceivedBy, new EmailAddress(address));
            SetProperty(message, ItemSchema.DateTimeCreated, DateTime.Now.AddMinutes(-10));
            SetProperty(message, ItemSchema.DateTimeReceived, DateTime.Now);

            return message;
        }

        static bool SetProperty(EmailMessage message,
             PropertyDefinition propertyDefinition,
             object value)
        {
            if (message == null)
                return false;

            // get value of PropertyBag property – that is wrapper
            // over dictionary of inner message’s properties
            var members = message.GetType().FindMembers(
                MemberTypes.Property,
                BindingFlags.NonPublic | BindingFlags.Instance,
                PartialName,
                "PropertyBag");
            if (members.Length < 1)
                return false;

            var propertyInfo = members[0] as PropertyInfo;
            if (propertyInfo == null)
                return false;

            var bag = propertyInfo.GetValue(message, null);
            members = bag.GetType().FindMembers(
                MemberTypes.Property,
                BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
                PartialName,
                "Properties");
            if (members.Length < 1)
                return false;

            // get dictionary of properties values
            var properties = ((PropertyInfo) members[0]).GetMethod.Invoke(bag, null);
            var dictionary = properties as Dictionary<PropertyDefinition, object>;
            if (dictionary == null)
                return false;

            dictionary[propertyDefinition] = value;
            return true;
        }

        static bool PartialName(MemberInfo info, Object part)
        {
            // Test whether the name of the candidate member contains the
            // specified partial name.
            return info.Name.Contains(part.ToString());
        }
    }
}
*************************************************


1. All used IP-addresses, names of servers, workstations, domains, are fictional and are used exclusively as a demonstration only.
2. Information is provided «AS IS».

13 thoughts on “Set up properties of EmailMessage

  1. Интересно, а можно тоже самое сделать, но не через managed API, а через Proxy классы. В моем проекте требуется тоже самое – создавать письма с возможностью задать любое поле.
    P.S. в проекте используется .Net 2.0

  2. Какой язык используется в проекте? Почему нельзя использовать managed API?
    Кстати, через PorpertyBag можно установить не все свойства объекта EmailMessage. Точнее можно посмотреть с помощью IL-Dasm.

    1. Согласно документации класса PropertyInfo, свойство GetMethod поддерживается в .Net Framework, начиная с версии 4.5. По-видимому, Вы используете в проекте младшую версию. Если нет возможности использовать версию 4.5 или выше, попробуйте использовать метод GetGetMethod().
      Кроме того, на следующей строке заметил ошибку в использовании класса Dictionary, поправил код.

      1. Спасибо за ответ.
        С 4.5 разобрался (на машине то стоит 4.5, де факто: в режиме отладки GetMethod я видел, но далеко не сразу смог понять, что надо и проект в 4.5 делать)
        GetGetMethod, кстати, попробовал – с ним не работало(компилировалось, но выпадало в exception на нем.)

        В итоге у объекта MailItem получилось выставить значения даты, но, увы, при попытке сохранить письмо в ящик – дата бралась текущая, а не выставленная. У Вас получалось сохранять созданные таким образом объекты?

        1. 1. О каком из полей-дат идет речь? Их много.
          2. А если так сделать – создать письмо, сохранить в почтовом ящике (потребуется “живой” Exchange Server), прочитать, исправить дату и сделать остальные манипуляции? Какая задача решается?
          Данный метод появился для эмуляции получения писем, когда физически Exchange сервер не используется.

          1. 1. Пробовал все 3 менять (DateTimeCreated, DateTimeReceived, DateTimeSent) – в объекте класса изменения проходят – при сохранении игнорируются.

            2. Пробовал создавать, а затем обновлять – в объекте опять все отлично меняется – в ящике – нет.

            Хм, у меня как раз есть живой сервер, даже два. Задача решается синхронизации почты между двумя ящиками на разных серверах – пока безуспешно.

          2. Синхронизация предполагается для всех изменений: получение, отправка, изменения, или только доставка писем?
            Я проконсультируюсь с коллегами как лучше решить подобную задачу. Возможно, копирование писем с помощью EWS не является самым удачным подходом. Ведь еще потребуется создавать сервис или задание, которые с указанной периодичностью будет синхронизировать изменения.

          3. Ну идея такая и была – сервис с периодичностью обходит ящики и синхронизирует изменения. И это должно работать пока идет миграция. Создать то сервис не проблема – мне бы письма как-то скопировать)

            А более глобально – в организации, куда я попал, два отдела “немного” работали не согласовано и вышло, что запущено 2 домена с двумя серверами exchange(lync,sharepoint и т.п.). В конце концов такая двойственность надоела – все ВЦ расформировали и собрали заново с задачей прийти к чему-то одному. Итого – глобальные миграции)

            Первоначальная идея была подключится к обоим серверам по ews и просто копировать почту (через MailItem.Copy), но, увы, это не сработало. Затем вот попробовал создавать копии писем.

            Кроме ews пробовали перемещать ящики через PowerShell, но данный сценарий отрабатывал, если в домене, куда идет перемещение, у пользователя еще нет ящика. (В моем случае есть практически у всех в обеих доменах)

          4. Коллеги рекомендуют для каждого пользователя использовать командлет Export-Mailbox, чтобы сконвертировать почтовый ящик в pst-файл. Затем командлетом Import-Mailbox импортировать письма в ящик “такого же” пользователя в целевом домене.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s