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».
Интересно, а можно тоже самое сделать, но не через managed API, а через Proxy классы. В моем проекте требуется тоже самое – создавать письма с возможностью задать любое поле.
P.S. в проекте используется .Net 2.0
Какой язык используется в проекте? Почему нельзя использовать managed API?
Кстати, через PorpertyBag можно установить не все свойства объекта EmailMessage. Точнее можно посмотреть с помощью IL-Dasm.
Не могу понять чего ему не хватает… https://www.dropbox.com/s/2tcgr9e04pfw8qu/error.png
Согласно документации класса PropertyInfo, свойство
GetMethod
поддерживается в.Net Framework
, начиная с версии 4.5. По-видимому, Вы используете в проекте младшую версию. Если нет возможности использовать версию 4.5 или выше, попробуйте использовать метод GetGetMethod().Кроме того, на следующей строке заметил ошибку в использовании класса
Dictionary
, поправил код.Спасибо за ответ.
С 4.5 разобрался (на машине то стоит 4.5, де факто: в режиме отладки GetMethod я видел, но далеко не сразу смог понять, что надо и проект в 4.5 делать)
GetGetMethod, кстати, попробовал – с ним не работало(компилировалось, но выпадало в exception на нем.)
В итоге у объекта MailItem получилось выставить значения даты, но, увы, при попытке сохранить письмо в ящик – дата бралась текущая, а не выставленная. У Вас получалось сохранять созданные таким образом объекты?
1. О каком из полей-дат идет речь? Их много.
2. А если так сделать – создать письмо, сохранить в почтовом ящике (потребуется “живой” Exchange Server), прочитать, исправить дату и сделать остальные манипуляции? Какая задача решается?
Данный метод появился для эмуляции получения писем, когда физически Exchange сервер не используется.
1. Пробовал все 3 менять (DateTimeCreated, DateTimeReceived, DateTimeSent) – в объекте класса изменения проходят – при сохранении игнорируются.
2. Пробовал создавать, а затем обновлять – в объекте опять все отлично меняется – в ящике – нет.
Хм, у меня как раз есть живой сервер, даже два. Задача решается синхронизации почты между двумя ящиками на разных серверах – пока безуспешно.
Синхронизация предполагается для всех изменений: получение, отправка, изменения, или только доставка писем?
Я проконсультируюсь с коллегами как лучше решить подобную задачу. Возможно, копирование писем с помощью EWS не является самым удачным подходом. Ведь еще потребуется создавать сервис или задание, которые с указанной периодичностью будет синхронизировать изменения.
Ну идея такая и была – сервис с периодичностью обходит ящики и синхронизирует изменения. И это должно работать пока идет миграция. Создать то сервис не проблема – мне бы письма как-то скопировать)
А более глобально – в организации, куда я попал, два отдела “немного” работали не согласовано и вышло, что запущено 2 домена с двумя серверами exchange(lync,sharepoint и т.п.). В конце концов такая двойственность надоела – все ВЦ расформировали и собрали заново с задачей прийти к чему-то одному. Итого – глобальные миграции)
Первоначальная идея была подключится к обоим серверам по ews и просто копировать почту (через MailItem.Copy), но, увы, это не сработало. Затем вот попробовал создавать копии писем.
Кроме ews пробовали перемещать ящики через PowerShell, но данный сценарий отрабатывал, если в домене, куда идет перемещение, у пользователя еще нет ящика. (В моем случае есть практически у всех в обеих доменах)
Коллеги рекомендуют для каждого пользователя использовать командлет Export-Mailbox, чтобы сконвертировать почтовый ящик в pst-файл. Затем командлетом Import-Mailbox импортировать письма в ящик “такого же” пользователя в целевом домене.
А настроить доверительные отношения и в рамках доверия перести ящики?
[…] The code of SetProperty method is listed here. […]
[…] ссылок на библиотеки по нестандартному пути. 4. Установка свойств объекта EmailMessage. 5. Создание объекта EmailMessage из .eml […]