'Save Mail in MIME format (*.eml) in Outlook Add-In

I want to write a little Outlook addin (C#), which saves a selected mail (MailItem) to disk in plain MIME format (.eml). The MailItem.SaveAs() method only allows to save in .msg format. Is there any other (simple) way, to save the mail in eml-format? I want to keep all details of the original mail.

I've read something about the Outlook WebServices. Maybe I can search the exchange server for the currently in outlook selected mail and receive it again from exchange and save it as .eml? What will I need for this option?

Is it possible to convert a saved .msg to .eml in an easy way (with keeping all details, headers and so on)?

I hope someone can help me with this problem, because I have spent a couple of hours on searching for a solution without any result.



Solution 1:[1]

You can either

  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`
    

Solution 2:[2]

Here's an IConvertSession proxy for C#:

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

Using it:

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://stackoverflow.com/a/6602066/2532356

Other fonts:

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Martijn Pieters
Solution 2 Community