先前分享過透過 EWS 從 Exchange 收信接任務執行作業的定期排程,最近生出小副本。程式需從 Mail 取出 HTML 解析,因格式並不統一,解析邏輯需要有點彈性,以便從不同格式中取出所需資訊(類似爬網頁,考驗 HtmlAgilityPack 跟 Regular Expression 技巧)。為了確保解析結果正確,我需要大量樣本在每次調整後重新驗證,以免為了改 A 把 B 弄壞。現成樣本好找,使用者 Outlook 收件匣由就有一大堆實例,最笨但有效的做法是請使用者一封封轉寄到 Exchange 信箱,再跑程式收下來。不過,能自動化處理的事搞成手工藝不是我的風格。Outlook 裡的郵件拖到桌面或檔案總管會自動轉存成 .msg 檔,請使用者將樣本 Mail 存成 .msg 再壓成 ZIP 檔一次寄給我,不需做苦工,剩下我來處理。

嘴上說得豪氣,我知道 .msg 可以用 Outlook 開啟,但要怎麼用程式讀取呀?以前沒玩過,直覺肯定有解,爬文也真找到不少選項:

由於屬一次性工作又在我的個人電腦執行就好,不想花時間研究新東西,我的電腦有現成的 Outlook,用 Outlook 解決最快。

第一步是在專案加入 COM 參照 - Microsoft Outlook 16.0 Object Library:

程式挺簡單,參考 MS Docs 上的範例,檢查 Outlook 是否已在執行中,若是就用現成的,否則 new Microsoft.Office.Interop.Outlook.Application() 建一個新的。 Application.Session 有個 OpenSharedItem() 方法,可用來開啟 .ics ( iCalendar 日曆數據交換約會資料)、.vcf (連絡人資訊) 或 .msg (電子郵件)。用 OpenSharedItem(.msg 路徑) 開啟 .msg 檔再轉型為 Microsoft.Office.Interop.Outlook.MailItem,接下來即可從 HtmlBody 屬性取得郵件 HTML 內容:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace MiscTools
{
    /// <summary>
    /// 從 Outloook 郵件 .msg 檔擷取 HTML 內容
    /// </summary>
    public static class MsgHtmlExtrator
    {
        public static Dictionary<string, string> ConvToHtmls(string[] msgFilePaths)
        {
            //若 Outlook 已開啟,用現成的 Instance,否則新建
            var existing = Process.GetProcessesByName("OUTLOOK").Any();
            Outlook.Application outlookApp =
                existing ? 
                Marshal.GetActiveObject("Outlook.Application") as Outlook.Application:
                new Outlook.Application();
            var result = new Dictionary<string, string>();
            foreach (var msgFilePath in msgFilePaths)
            {
                var mail = (Outlook.MailItem)outlookApp.Session.OpenSharedItem(msgFilePath);
                result.Add(msgFilePath, mail.HTMLBody);
            }
            if (!existing) outlookApp.Quit();
            return result;
        }

        public static string ConvToHtml(string msgFilePath)
        {
            return ConvToHtmls(new string[] { msgFilePath }).First().Value;
        }

    }
}

由於啟動與執行 Outlook 成本較高,大量轉換時會採取一次傳入所有 .msg 路徑批次處理後傳回的策略,以減少資源損耗。搞定收工~

Example of reading HTML from Outlook .msg file with Outlook API.


Comments

Be the first to post a comment

Post a comment