在Outlook附加组件中将邮件保存为MIME格式(*.eml)

3
我想编写一个小的Outlook插件(C#),它可以将选定的邮件(MailItem)以纯MIME格式(.eml)保存到磁盘上。 MailItem.SaveAs()方法只允许以.msg格式保存。 有没有其他(简单)方法可以将电子邮件保存为eml格式? 我希望保留原始邮件的所有细节。
我读过一些关于Outlook WebServices的内容。 也许我可以在交换服务器上搜索当前在Outlook中选择的邮件,然后再次从交换服务器接收它并保存为.eml? 我需要什么来实现这个选项?
是否可能以简单的方式将保存的.msg转换为.eml(保留所有详细信息,标头等)?
我希望有人能够帮助我解决这个问题,因为我已经花费了几个小时寻找解决方案,但没有任何结果。
2个回答

7
这里是用于C#的IConvertSession代理:
using Microsoft.Office.Interop.Outlook;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Exchange.Export.MAPIMessageConverter
{
    internal class MAPIMethods
    {
        [Flags]
        public enum MAPITOMIMEFLAGS
        {
            CCSF_SMTP = 0x0002,
            CCSF_NOHEADERS = 0x0004,
            CCSF_USE_TNEF = 0x0010,
            CCSF_INCLUDE_BCC = 0x0020,
            CCSF_8BITHEADERS = 0x0040,
            CCSF_USE_RTF = 0x0080,
            CCSF_PLAIN_TEXT_ONLY = 0x1000,
            CCSF_NO_MSGID = 0x4000,
        }

        [Flags]
        public enum CLSCTX
        {
            CLSCTX_INPROC_SERVER = 0x1,
            CLSCTX_INPROC_HANDLER = 0x2,
            CLSCTX_LOCAL_SERVER = 0x4,
            CLSCTX_REMOTE_SERVER = 0x10,
            CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
            CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
            CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER
        }


        public static Guid CLSID_IConverterSession = new Guid("{4e3a7680-b77a-11d0-9da5-00c04fd65685}");

        public static Guid IID_IConverterSession = new Guid("{4b401570-b77b-11d0-9da5-00c04fd65685}");

        public enum ENCODINGTYPE
        {
            IET_BINARY = 0,
            IET_BASE64 = 1,
            IET_UUENCODE = 2,
            IET_QP = 3,
            IET_7BIT = 4,
            IET_8BIT = 5,
            IET_INETCSET = 6,
            IET_UNICODE = 7,
            IET_RFC1522 = 8,
            IET_ENCODED = 9,
            IET_CURRENT = 10,
            IET_UNKNOWN = 11,
            IET_BINHEX40 = 12,
            IET_LAST = 13
        }

        public enum MIMESAVETYPE
        {
            SAVE_RFC822 = 0,
            SAVE_RFC1521 = 1
        }

        [ComVisible(false)]
        [ComImport()]
        [Guid("00020307-0000-0000-C000-000000000046")]
        [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IMessage
        {
        }

        [ComImport]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        [Guid("4b401570-b77b-11d0-9da5-00c04fd65685")]
        public interface IConverterSession
        {
            [PreserveSig]
            int Placeholder0();

            [PreserveSig]
            uint SetEncoding(
            [In, MarshalAs(UnmanagedType.I4)] ENCODINGTYPE DispId
            );

            [PreserveSig]
            int Placeholder1();

            [PreserveSig]
            uint MIMEToMAPI(
                [In, MarshalAs(UnmanagedType.Interface)]
                Stream pstm,
                [Out, MarshalAs(UnmanagedType.Interface)]
                MailItem pmsg,
                object pszSrcSrv,
                uint ulFlags
            );

            [PreserveSig]
            uint MAPIToMIMEStm(
                [In, MarshalAs(UnmanagedType.Interface)]
                IMessage pmsg,
                [Out, MarshalAs(UnmanagedType.Interface)]
                IStream pstm,
                MAPITOMIMEFLAGS ulFlags
            );

            [PreserveSig]
            int Placeholder2();

            [PreserveSig]
            int Placeholder3();

            [PreserveSig]
            int Placeholder4();

            [PreserveSig]
            int SetTextWrapping(
                bool fWrapText,
                uint ulWrapWidth
            );

            [PreserveSig]
            uint SetSaveFormat(
                [In, MarshalAs(UnmanagedType.I4)]
                MIMESAVETYPE mstSaveFormat
            );

            [PreserveSig]
            int Placeholder5();

            [PreserveSig]
            int Placeholder6();
        }
    }
}

使用它:

private Stream GetEmlStream(Outlook.MailItem mail)
{
    Type converter = Type.GetTypeFromCLSID(MAPIMethods.CLSID_IConverterSession);
    object obj = Activator.CreateInstance(converter);
    MAPIMethods.IConverterSession session = (MAPIMethods.IConverterSession)obj;

    if (session != null)
    {
        uint hr = session.SetEncoding(MAPIMethods.ENCODINGTYPE.IET_QP);
        hr = session.SetSaveFormat(MAPIMethods.MIMESAVETYPE.SAVE_RFC822);
        var stream = new ComMemoryStream();
        hr = session.MAPIToMIMEStm((MAPIMethods.IMessage)mail.MAPIOBJECT, stream, MAPIMethods.MAPITOMIMEFLAGS.CCSF_SMTP);
        if (hr != 0)
            throw new ArgumentException(There are some invalid COM arguments");

        stream.Position = 0;

        return stream;
    }

    return null;
}

ComMemoryStream: https://dev59.com/FljUa4cB1Zd3GeqPWPnh#6602066

其他字体:

http://www.pcreview.co.uk/threads/iconvertersession-in-c.3716714/

http://www.microsoft-questions.com/microsoft/Plaform-SDK-Mapi/31018989/mimetomapi-and-mapitomimestm-method-take-a-pointer-to-an-imessage-extended.aspx


谢谢!我已经尝试了很长时间来解决这个问题!它的效果比Redemption.RDOSession好得多。 - Jeppe Spanggaard
请注意,从Outlook 2016开始,只有在outlook.exe地址空间(COM插件)内运行时才能使用IConverterSession接口。在单独的进程中运行时,无法创建该接口的实例。 - Dmitry Streblechenko

3
你可以选择
  1. Create MIME file explicitly in your code one property at a time. You can also use existing MIME converters (I used Lumisoft in the past) - but they won't convert Outlook messages in a single call; you will need to expliiclty build all the headers and MIME parts.

  2. Use IConverterSession object (C++ or Delphi only) - this is the same MIME converter used by Outlook. You can play with it in OutlookSpy (I am its author) - click IConverterSession button. Note that as of Outlook 2016, IConverterSession interface can be used only if running inside the outlook.exe address space (COM addin). You cannot create an instance of that interface when running in a separate process.

  3. Use Redemption (I am also its author) and its RDOMail.SaveAs or SafeMailItem.SaveAs methods - it can save in the MIME format (olRfc822) along with a dozen or so other formats. It uses IConverterSession object when it is available (Outlook 2003 and up) or its own converter otherwise. The following script (VBS) will save the currently selected message in Outlook as an EML file

      set Session = CreateObject("Redemption.RDOSession")
      Session.MAPIOBJECT = Application.Session.MAPIOBJECT
      set rItem = Session.GetMessageFromID(Application.ActiveExplorer.Selection(1).EntryID)
      rItem.SaveAs "c:\temp\test.eml", 1024`
    

1
谢谢你的回答。我试图手动“转换”这些消息,但似乎很难转换所有的消息(包括所有的标题等)。我不想使用外部库,也不想花费太多时间在这上面,所以我不想用C++编写自己的转换器。因此,我最终决定使用MailItem.SaveAs()方法提供的.msg格式。我们可以接受这种方式。 - Robert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接