В настоящее время я пишу службу Windows для входа в конкретную учетную запись Exchange, получения любых новых сообщений электронной почты, их анализа и сохранения электронной почты в соответствующей папке.
Все работает отлично, кроме сохранения электронной почты.
Соответствующие блоки кода (блоки try / catch и ненужные элементы удалены, чтобы сократить их): -
Настройка службы
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials(emailAddress, password);
service.AutodiscoverUrl(emailAddress, RedirectionUrlValidationCallback);
CheckForNewEmails(service);
Получение и проверка новых писем
private static void CheckForNewEmails(ExchangeService service)
{
int offset = 0;
int pageSize = 50;
bool more = true;
ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
view.PropertySet = PropertySet.IdOnly;
FindItemsResults<Item> findResults;
List<EmailMessage> emails = new List<EmailMessage>();
while (more)
{
findResults = service.FindItems(WellKnownFolderName.Inbox, view);
foreach (var item in findResults.Items)
{
emails.Add((EmailMessage)item);
}
more = findResults.MoreAvailable;
if (more)
{
view.Offset += pageSize;
}
}
if (emails.Count > 0)
{
PropertySet properties = (BasePropertySet.FirstClassProperties);
service.LoadPropertiesForItems(emails, properties);
var mailItems = new List<DatabaseService.MailItem>();
var dbService = new DatabaseService();
var defaultUser = dbService.GetDefaultUser(defaultUserID);
foreach (var email in emails)
{
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
}
}
}
Поиск, с кем это должно быть связано
public void FindLinks(ExchangeService service, ref MailItem mailItem, User defaultUser)
{
string address = mailItem.Sender;
// get file hash
var tempPath = Path.GetTempPath();
var fileName = GetFilenameFromSubject(mailItem);
var fullName = Path.Combine(tempPath, fileName);
SaveAsEML(service, mailItem, fullName);
var sha = new SHA256Managed();
mailItem.Hash = Convert.ToBase64String(sha.ComputeHash(File.OpenRead(fullName)));
File.Delete(fullName);
using (var db = DatabaseHelpers.GetEntityModel())
{
// Do all the linking stuff
}
}
И, наконец, проблемная область: сохранение файла на диск (в данном случае во временную папку, чтобы я мог получить хэш файла, чтобы проверить, не является ли он дубликатом (например, CC'd и т. Д.))
Просматривая StackOverflow и различные другие источники, кажется, есть два способа сделать это: -
1) Использование метода MailItem.Load
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
mailItem.mail.Load(new PropertySet(ItemSchema.MimeContent));
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Используя приведенный выше код, файл создан и имеет правильное содержание.
Однако попытка получить доступ к любому из свойств электронной почты ПОСЛЕ этой точки приводит к сбою Null Exception (большой сбой тоже, ловушка Try / Catch или UnhandledException не поднимет его, просто убивает Сервис)
В приведенном выше коде в этой строке происходит сбой всего сервиса: -
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
В частности, ссылка на электронную почту. DateTimeSent
Я добавил диагностический код прямо перед этой строкой: -
if (email == null) { Log it's null; } else { Log NOT null;}
if (email.DateTimeSent == null) { Log it's null; } else { Log NOT null; }
В первой строке записывается НЕ ноль, поэтому электронная почта все еще существует.
Однако вторая строка мгновенно вылетает с нулевой ошибкой исключения, ничего не регистрируя.
Если я закомментирую строку SaveAsEML (service, mailItem, fullName); из FindLinks тогда все работает отлично (за исключением, конечно, файл не сохраняется).
2) Привязка набора свойств
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Делая это таким образом, ничего не вылетает, оно прекрасно проходит через каждое письмо и может ссылаться на email.DateTimeSent и все другие свойства.
К сожалению, он создает файлы нулевой длины, без содержимого.
Вот уже несколько часов я бьюсь головой об стену (мне потребовался час, чтобы добавить диагностику повсюду, чтобы отследить сбой, происходящий при ссылке на свойства), и это, несомненно, что-то тривиальное, что я упустил из виду, так что если какой-то душа могла бы указать на мою глупость, я был бы очень благодарен!
Редактировать:
Мне удалось достаточно легко обойти вышеуказанную проблему, просто сохранив значение свойств перед их использованием: -
foreach (var email in emails)
{
var dateTimeSent = email.DateTimeSent;
var sender = email.Sender;
var subject = email.Subject;
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID,
dateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
sender,
subject,
mailItem.Hash ?? "No Hash",
mailItem.LinkString ?? "No Linkstring"
));
}
Это позволило мне использовать метод MailItem.Load, который сохранил файл, и, таким образом, сделать то, что я делал в этом конкретном случае.
Однако есть и другие вещи, которые необходимо будет сделать этой Службе, и я действительно не хочу сохранять копию каждого отдельного свойства, к которому мне нужно получить доступ.