dcsimg
 

Saving an E-mail as an .eml File in C#

Friday Apr 13th 2018 by Robert Gravelle

Discover a new "Outlook," and write some C# code that uses the .NET SmtpClient class to programmatically save e-mails in the EML format.

Outlook's default file format when saving an e-mail to disk is MSG (.msg). Meanwhile, Microsoft Outlook Express and some other e-mail programs, such as Mozilla Thunderbird, save their files in the EML (.eml) format, which follows the MIME RFC 822 standard. Recent versions of Outlook can open EML files, but if you're looking to work with them within Outlook, you'll have to put in a bit of work. In today's article, we'll write some C# code that uses the .NET SmtpClient class to programmatically save e-mails in the EML format.

Saving an E-mail

There are several ways to save an e-mail in EML format, but one of the simplest that I have come across comes from the Stackoverflow site. It sets the SmtpClient's DeliveryMethod to a specified directory. It just happens that the SmtpClient saves e-mails in EML format.

Here's the start of the Program.cs file, which is part of a C# .NET console application:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Mail;
using System.IO;
using System.Reflection;

namespace MailTests
{
   static class Program
   {
      static void Main(string[] args)
      {
         string to       = @"rob@robgravelle.com";
         string from     = @"bifft@gmail.com";
         string subject  = @"Working with the .eml file type in
                             C#.";
         string body     = @"Using the SmtpClient class, you can
                             save an e-mail message in .eml format
                             very easily.";
         string emailDir = @"I:\\My Documents\\articles\\codeguru
                             \\C#\\Saving an E-mail\\SavedMail";
         string msgName  = @"msg.eml";

         Console.WriteLine("Saving e-mail...");
      }
   }
}

Saving the E-mail

The demo app just creates the test e-mail programmatically using our constants above. UseDefaultCredentials is set to true so that we don't have to provide any credentials. Then, the DeliveryMethod is set to use the specified pickup directory. Mail messages in the pickup directory are automatically sent by a local SMTP server (if present), such as IIS. The directory is set in the following line where the PickupDirectoryLocation is set to the emailDir constant.

The Send() invocation is enclosed within a try/catch block because it could fail under certain circumstances, such as not having write permissions on the directory, etcetera. The Console.ReadLine() call waits for user input so that the console doesn't immediately close.

Console.WriteLine("Saving e-mail...");
using (var client = new SmtpClient())
{
   MailMessage msg = new MailMessage(from, to, subject, body);
   client.UseDefaultCredentials = true;
   client.DeliveryMethod =
      SmtpDeliveryMethod.SpecifiedPickupDirectory;
   client.PickupDirectoryLocation = emailDir;
   try
   {
      client.Send(msg);
   }
   catch (Exception ex)
   {
      Console.WriteLine("Exception caught: {0}", ex.ToString());
         Console.ReadLine();
      System.Environment.Exit(-1);
   }
}

Setting the File Name

The SmtpClient saves the file with a random file name comprised of a random Guid, courtesy of Guid.NewGuid(), plus the ".eml" extension, something like "263cf925-aa20-40b4-a860-f03c8ceb9537.eml". This guarantees a unique name, but if you would like something a little more descriptive, there are ways to rename the file. Or if you're really ambitious, use Reflection to construct your own internal MailWriter. The Stackoverflow solution on which my code is based employed Guid.NewGuid() to create a temp directory. This strategy avoids the possibility of overwriting another file with the same name—at least until you move the file to its final destination!

My solution deals with that problem by asking the user to make a decision. The drawback to this approach is that it does not lend itself to unsupervised batch jobs. Without using a temp folder, you also need to be able to identify the new file. I did that by sorting files by LastWriteTime in descending order, so that the new file would be at the top (i.e. first).

var defaultMsgPath = new DirectoryInfo(emailDir).GetFiles()
      .OrderByDescending(f => f.LastWriteTime)
      .First();
var realMsgPath = Path.Combine(emailDir, msgName);
try
{
   File.Move(defaultMsgPath.FullName, realMsgPath);
   Console.WriteLine("Message saved.");
}
catch (System.IO.IOException e)
{
   Console.WriteLine("File already exists. Overwrite it? Y/N");

   var test = Console.ReadLine();
   if (test == "y" || test == "Y")
   {
      Console.WriteLine("Overwriting existing file...");
      File.Delete(realMsgPath);
      File.Move(defaultMsgPath.FullName, realMsgPath);
      Console.WriteLine("Message saved.");
   }
   else
   {
      Console.WriteLine("Exiting Program without saving file.");
   }
}
Console.WriteLine("Press any key to exit.");
Console.ReadLine();

Here is the entire Program.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Mail;
using System.IO;
using System.Reflection;

namespace MailTests
{
   static class Program
   {
      static void Main(string[] args)
      {
         string to       = @"rob@robgravelle.com";
         string from     = @"bifft@gmail.com";
         string subject  = @"Working with the .eml file type in
                             C#.";
         string body     = @"Using the SmtpClient class, you can
                             save an e-mail message in .eml format
                             very easily.";
         string emailDir = @"I:\\My Documents\\articles\\codeguru
                             \\C#\\Saving an E-mail\\SavedMail";
         string msgName  = @"msg.eml";

         Console.WriteLine("Saving e-mail...");
         using (var client = new SmtpClient())
         {
            MailMessage msg = new MailMessage(from, to, subject,
               body);
            client.UseDefaultCredentials = true;
            client.DeliveryMethod =
               SmtpDeliveryMethod.SpecifiedPickupDirectory;
            client.PickupDirectoryLocation = emailDir;
            try
            {
               client.Send(msg);
            }
            catch (Exception ex)
            {
               Console.WriteLine("Exception caught: {0}",
                  ex.ToString());
               Console.ReadLine();
               System.Environment.Exit(-1);
            }
         }

         var defaultMsgPath = new
            DirectoryInfo(emailDir).GetFiles()
               .OrderByDescending(f => f.LastWriteTime)
               .First();
         var realMsgPath = Path.Combine(emailDir, msgName);
         try
         {
            File.Move(defaultMsgPath.FullName, realMsgPath);
            Console.WriteLine("Message saved.");
         }
         catch (System.IO.IOException e)
         {
            Console.WriteLine("File already exists. Overwrite
               it? Y/N");

            var test = Console.ReadLine();
            if (test == "y" || test == "Y")
            {
               Console.WriteLine("Overwriting existing file...");
               File.Delete(realMsgPath);
               File.Move(defaultMsgPath.FullName, realMsgPath);
               Console.WriteLine("Message saved.");
            }
            else
            {
               Console.WriteLine("Exiting Program without saving
                  file.");
            }
         }
         Console.WriteLine("Press any key to exit.");
         Console.ReadLine();
      }
  }
}

Conclusion

As we saw here today, saving a MailMessage as an EML file by using the .NET SmtpClient class is relatively straightforward. That being said, an Outlook MailItem and .NET Framework MailMessage are very different things. In upcoming articles, we'll see how to save an Outlook MailItem as an .eml file as well as how to send an e-mail from an EML file.

Home
Mobile Site | Full Site