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.

Friday, May 06, 2016

C# Switch on Type

I want to write readable code like below to handle different sub types. Let’s say if I want to take a shower for my pets. Depending on the pet type, I will like to handle it differently. As you can tell, if statements can do the work but it will be very messy. I would like to use switch statement. However, C# compiler doesn’t want to get into this business. That’s alright. I will like to do it myself. Here is a clean solution. The ultmiate code is very easy to read. No if statements. Zero. None. Nil. There are 3 simple steps.

myPet.Switch()
     .Case<Dog>(x => HandleDog(x))
     .Case<Cat>(x => HandleCat(x))
     .Case<Parrot>(x => HandleParrot(x))
     .Default<Pet>(x => HandleDefault(x));

Step 1: Create a Switch class.

public class Switch
{
    public Switch(object o)
    {
        Target = o;
    }

    public object Target { get; set; }
}

 

Step 2: Create extension methods to support fluent API, supporting, predict, fall through, and default.

public static class SwitchExtensions
{
    public static Switch Case<T>(this Switch switcher, 
        Action<T> action) where T : class
    {
        return Case(switcher, o => true, action, false);
    }

    public static Switch Case<T>(this Switch switcher, 
        Action<T> action, 
        bool fallThrough) where T : class
    {
        return Case(switcher, o => true, action, fallThrough);
    }

    public static Switch Case<T>(this Switch switcher, 
        Func<T, bool> predicate, 
        Action<T> action) where T : class
    {
        return Case(switcher, predicate, action, false);
    }

    public static Switch Case<T>(this Switch s, 
        Func<T, bool> predicate, 
        Action<T> action, 
        bool fallThrough) where T : class
    {
        if (s == null)
        {
            return null;
        }

        var t = s.Target as T;
        if (t == null)
        {
            return s;
        }

        if (!predicate(t))
        {
            return s;
        }

        action(t);
        return fallThrough ? s : null;
    }

    public static void Default<T>(this Switch switcher, 
        Action<T> action) where T : class
    {
        if (switcher == null)
        {
            return;
        }

        var t = switcher.Target as T;
        if (t == null)
        {
            return;
        }

        action(t);
    }
}
Step 3: Finally, add an extension method for System.Object so that any object can call Switch() method.
public static class ObjectSwitchExtensions
{
    public static Switch Switch(this object Object)
    {
        return new Switch(Object);
    }
}

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 ...