Tuesday, October 25, 2016

Launch Prepopulated New Email Message in WPF/WinForm

This article discussed three options to launch prepopulated New Email Window from your WPF/WinForm desktop application. The prepopulated fields can include From, To, Subject, Content, and Attachments etc.

 

Option 1: MailTo URL

Pros: MailTo protocol is the easiest and the cleanest way to launch the New Mail Message window. All you need to do is to create the URL and launch it using in a separated process.

        var url = @"mailto:user@example.com?subject=Message Title&body=Message Content";
        System.Diagnostics.Process.Start(url);

Cons: There is no official way to attach files using "mailto" protocol, likely because of the security reasons. It’s not support HTML content. More information can be found in MSDN.

 

Option 2: EML File

Pros: Save System.Net.Mail.MailMessage to a .EML file and open the .EML file with default mail application. It supports attachment. It supports HTML content.

        var mailMessage = new MailMessage();
        mailMessage.From = new MailAddress("someone@yourdomain.com");
        mailMessage.Subject = "Your subject here";
        mailMessage.IsBodyHtml = true;
        mailMessage.Body = "<span style='font-size: 12pt; color: red;'>My HTML formatted body</span>";
        mailMessage.Attachments.Add(new Attachment("C://Myfile.pdf"));
        var filename = "C://Temp/mymessage.eml";
        mailMessage.Save(filename);
        Process.Start(filename);

        public static void Save(this MailMessage message, string filename, bool addUnsentHeader = true)
        {
            using (var filestream = File.Open(filename, FileMode.Create))
            {
                if (addUnsentHeader)
                {
                    var binaryWriter = new BinaryWriter(filestream);
                    binaryWriter.Write(System.Text.Encoding.UTF8.GetBytes("X-Unsent: 1" + Environment.NewLine));
                }

                var assembly = typeof(SmtpClient).Assembly;
                var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");

                var mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);

                var mailWriter = mailWriterContructor.Invoke(new object[] { filestream });
                var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);

                sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { mailWriter, true, true }, null);

                var closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic);

                closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null);
            }
        }

 

Cons: Some email servers can’t send out email because of security settings for From: and Sender:. You will probably get exceptions like this.

“You can't send a message on behalf of this user unless you have permission to do so. Please make sure you're sending on behalf of the correct sender, or request the necessary permission. If the problem continues, please contact your helpdesk.”

 

Option 3: Microsoft.Office.Interop.Outlook

Pros: A feasible option is to utilize Microsoft.Office.Interop.Outlook. I’ve tested different versions for Microsoft.Office.Interop.Outlook, for example 12.0.4518.1014 and 15.0.4797.1003, using the nugget packages. They all work either Office 2010 (14.0.x) or Office 365 (16.0.x). It looks like the COM API hasn’t changed from 14.0 to 16.0 to create new mail item.

With the reference property set to Embed Interop Types, the compiler will include the COM calls directly into Atlas assembly. Therefore, we don’t have to ship a specific version of a Microsoft Office Interop assembly with our product. Exceptions can be caught if any.

            
     var outlookType = Type.GetTypeFromProgID("Outlook.Application");
            if (outlookType == null)
            {
                throw new System.Exception("Microsoft Office Outlook is not installed.");
            }
            
            var oApp = new Application();
            var oMsg = (MailItem) oApp.CreateItem(OlItemType.olMailItem);
            oMsg.To = "someone@yourdomain.com";
            oMsg.Subject = subject;
            oMsg.BodyFormat = OlBodyFormat.olFormatHTML;
            oMsg.HTMLBody = ""; 
            oMsg.Attachments.Add(fullPath, OlAttachmentType.olByValue, Type.Missing, Type.Missing);
            oMsg.Display(false); 

 

Cons: It only works for Outlook.

Thumbs Up to GitHub Copilot and JetBrains Resharper

Having used AI tool GitHub Copilot since 08/16/2023, I’ve realized that learning GitHub Copilot is like learning a new framework or library ...