I was tasked with a tricky issue in sending emails. Due to security concerns, the client’s IT team was not willing to share SMTP information for their mail settings and was only willing to set up an account in Outlook directly on a dedicated machine without sharing the password with us to send the emails. The client’s ask was to send emails through Outlook without letting users see the emails or Outlook itself.

Installing Office Interop for Outlook

Sending emails through Outlook can be done using Microsoft.Office.Interop.Outlook but the documentation is really lacking. If you need to do the same, I hope this will save you the hours of time it took me to figure out what ends up not being complex code.

Create a new desktop application project in Visual Studio. Install the Microsoft Office Interop for Outlook. I used the NuGet package manager to install it since it wasn’t present on my system:

Install-Package Microsoft.Office.Interop.Outlook

Automating E-mails using C#

I created a static class to send the email through Outlook. Note that my error handling code was replaced with Debug.Writeline. Remember to modify it to handle errors or implement logging so it doesn’t fail silently.

Email.cs:

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Outlook;
using Exception = System.Exception;

namespace Email.classes
{
    public class Email
    {
        public static void SendWithEmbeddedImages(string to, string subject, string htmlMessage)
        {
            var missing = Type.Missing;

            Application oOutlook = null;
            NameSpace oNS = null;
            Folder oCtFolder = null;
            Items oCts = null;
            MailItem msg = null;


            var sHeaderPath = Path.Combine(Environment.CurrentDirectory, "emails", "header.jpg");
            var sLogoPath = Path.Combine(Environment.CurrentDirectory, "emails", "logo.jpg");

            try
            {
                // Create an Outlook application.
                oOutlook = new Application();

                // Get the namespace.
                oNS = oOutlook.GetNamespace("MAPI");

                //Assumes MAPI profile name is Outlook
                oNS.Logon("Outlook", missing, false, true);
                msg = (MailItem) oOutlook.CreateItem(OlItemType.olMailItem);

                var attachHeader = msg.Attachments.Add(sHeaderPath, OlAttachmentType.olEmbeddeditem);
                var attachLogo = msg.Attachments.Add(sLogoPath, OlAttachmentType.olEmbeddeditem);

                attachLogo.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E", "logo");
                attachHeader.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E",
                    "header");

                msg.Subject = subject;
                msg.To = to;

                msg.BodyFormat = OlBodyFormat.olFormatHTML;
                msg.HTMLBody = htmlMessage;

                //Show email
                msg.Display();

                //Send email
                //((Outlook._MailItem)msg).Send();

                oNS.Logoff();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Automate Outlook throws the error: {0}", ex.Message);
            }
            finally
            {
                // Manually clean up the explicit unmanaged Outlook COM resources by  
                // calling Marshal.FinalReleaseComObject on all accessor objects. 
                // See http://support.microsoft.com/kb/317109.

                if (msg != null)
                {
                    Marshal.FinalReleaseComObject(msg);
                    msg = null;
                }
                if (oCts != null)
                {
                    Marshal.FinalReleaseComObject(oCts);
                    oCts = null;
                }
                if (oCtFolder != null)
                {
                    Marshal.FinalReleaseComObject(oCtFolder);
                    oCtFolder = null;
                }
                if (oNS != null)
                {
                    Marshal.FinalReleaseComObject(oNS);
                    oNS = null;
                }
                if (oOutlook != null)
                {
                    Marshal.FinalReleaseComObject(oOutlook);
                    oOutlook = null;
                }
            }
        }
    }
}

Example on how to call the class:

 var sEmailPath = Path.Combine(Environment.CurrentDirectory, "emails", "single.html");

            var htmlMessage = "";
            if (File.Exists(sEmailPath))
            {
                //Load HTML from file
                htmlMessage = File.ReadAllText(sEmailPath);
            }

            Email.SendWithEmbeddedImages("toaddress@test.com", "Outlook Automation Test", htmlMessage);

email.html:

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
</head>

<body>
    <table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
        <tr>
            <td><img src="cid:header"></td>
        </tr>
        <tr>
            <td>
                Hello world!
            </td>
        </tr>
        <tr>
            <td><img src="cid:logo"></td>
        </tr>
    </table>
</body>

</html>

Important Notes and Gotchas

  • CIDs need to be unique. I’ve seen them on all other posts with the format “file.extension@{random #}” but keeping it simple like in the code above worked for me with no issues with Outlook 2016. I did not test on older versions of Outlook to confirm as I no longer have access to them.
  • Outlook ignores font rules in the HTML/CSS you code in the email and defaults to Times New Roman. I know Outlook uses the Word renderer but I have no idea why and the only solution I found was to update the default font in Microsoft Word. Yes, to change the font in Outlook, you’ll need to update the default font in Word. Here’s how to set it:
    • Open Word
      Go to Options -> Advanced -> Web Options
      Change the default font in the Fonts tab
  • Outlook only supports a subset of HTML so don’t forget to test and verify everything as most CSS formatting won’t work in Outlook.
How to Fix ‘Converter Failed to Save File’ with Excel 2016 How to Prevent Raspberry Pi Zero from Blanking or Sleeping
View Comments
  • Above code not working for Office 2016..

    • Hi Phaneendra, can you elaborate more on the error you’re getting? I haven’t tested the code since I posted it so it’s quite possible Microsoft updated or changed something in the newer versions of Office.

  • Hi Greg, first off thanks for the work you put into this, it saved me a lot of time.

    I think what Phaneendra is referring to might be from how you are generating filepath strings, it uses Environment.CurrentDirectory but if you are in a visual studio project, its directory is in bin\Debug so you will get a file not found exception. I hardcoded the path as a path-snippet and let the other path strings use path.combine to join the folder path and the filename – not a universal solution but works in my case.

    old: //var sHeaderPath = Path.Combine(Environment.CurrentDirectory, “emails”, “header.jpg”);

    declared in main: string sEmailResourcesPath = “path string to your email resources folder here”;
    Email.SendWithEmbeddedImages(“test@testdomain.com”, “Outlook Automation Test”, htmlMessage, sEmailResourcesPath);

    and inside Email.cs (after updating params on SendWithEmbeddedImages)

    changed

    var sHeaderPath = Path.Combine(Environment.CurrentDirectory, “emails”, “header.jpg”);

    to:

    var sHeaderPath = Path.Combine(sEmailResourcesPath, “header.jpg”);

    and that Debug.WriteLine doesnt display the message, try using:

    Debug.WriteLine($”Automate Outlook throws the error: {ex.Message}”);

    There is some other refactoring to do, but it works well for me after making a couple adjustments.

    Thanks again for the work and testing you put into this!