В настоящее время я пишу службу 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
));
В частности, ссылка на email.DateTimeSent
Я добавил диагностический код непосредственно перед этой строкой: -
if (email == null) { Log it's null; } else { Log NOT null;}
if (email.DateTimeSent == null) { Log it's null; } else { Log NOT null; }
В первой строке записано НЕ значение 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, который сохранил файл, и, таким образом, делать то, что мне нужно в этом конкретном случае.
Однако есть и другие вещи, которые эта Служба должна будет сделать, и я действительно не хочу сохранять копию каждого отдельного свойства, к которому мне нужно получить доступ.