C # Сохранение файла Exchange .EML из службы Windows

В настоящее время я пишу службу 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, который сохранил файл, и, таким образом, делать то, что мне нужно в этом конкретном случае.

Однако есть и другие вещи, которые эта Служба должна будет сделать, и я действительно не хочу сохранять копию каждого отдельного свойства, к которому мне нужно получить доступ.


person LMS    schedule 17.09.2018    source источник


Ответы (1)


Причина, по которой это не удастся

    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);
        }
    }

Это то, что вы использовали Bind для загрузки сообщения с MimeContent в переменной электронной почты, а затем вы его не использовали. mailItem.mail вообще не будет связан с переменной электронной почты, созданной в рамках этой операции (даже если они являются одним и тем же объектом на сервере). Локально это всего лишь две отдельные переменные. EWS является клиент-серверным, поэтому вы делаете запрос, и управляемый API возвращает локальный объект, представляющий результат операции. Но этот объект отключен, поэтому, когда вы выполняете привязку выше, он просто генерирует другой клиентский объект для представления результата этой операции. например, так что выше должно было быть

     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(email.MimeContent.Content, 0, email .MimeContent.Content.Length);
        }
    }
person Glen Scales    schedule 18.09.2018