C # Сохранение файла Exchange .EML из службы Windows - PullRequest
0 голосов
/ 17 сентября 2018

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

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

1 Ответ

0 голосов
/ 18 сентября 2018

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

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