Mobile Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Pushing the Limits of the Windows Phone SDK and Sending Files via EmailComposeTask

05.27.2012
| 6877 views |
  • submit to reddit

I was thinking that it is a shame that I cannot attach files to an email via EmailComposeTask – and indeed, I am not the only one thinking about this:

image

EmailComposeTask won’t allow you to send attachments, but this doesn’t mean that you cannot send files through it. .NET Framework has these two amazing methods: Convert.ToBase64String and Convert.FromBase64String. The first will allow the developer to convert a byte array (byte[]) to a Base64-encoded string, the other one will do the same operation in reverse.

Let’s say that I want to send a picture from my phone. I am going to use CameraCaptureTask:

Microsoft.Phone.Tasks.CameraCaptureTask task = new Microsoft.Phone.Tasks.CameraCaptureTask();
task.Completed += new EventHandler(task_Completed);
task.Show();

Whenever a photo is captured, I have direct access to the resulting stream. What I can do at that point, is get the byte array from it, convert it to Base64 and set is as the body for EmailComposeTask:

void task_Completed(object sender, Microsoft.Phone.Tasks.PhotoResult e)
{
    int read = 0;
    int part = 0;
    byte[] buffer = new byte[22000];

    while ((read = e.ChosenPhoto.Read(buffer,0,buffer.Length)) > 0)
    {
        Thread.Sleep(10000);

        part++;
        string bsfData = Convert.ToBase64String(buffer);

        Debug.WriteLine(bsfData.Length);

        EmailComposeTask task = new EmailComposeTask();
        task.To = "ddelimarsky@live.com";
        task.Subject = "Part " + part.ToString();
        task.Body = bsfData;
        task.Show();
    }

}

A couple of stress points here:

  • There is a content length restriction currently set to 64k (for the message body), therefore I have to split the entire captured byte array in chunks that are ready to be sent out. If ignored, the following exception is thrown:

    image
  • This ultimately creates a multi-part email (read: 22 parts for a 2 megapixel photo).
  • The Thread.Sleep(10000) creates a lag of 10 seconds between EmailComposeTask invocations, because there is no completion notifier event handler for this specific task type. The user has to quickly hit send in this case.
  • Without a sleeping thread, I get this:

    image

Once all parts are sent out, on the receiving end a simple console application like this will perform the Base64-to-content conversion:

using System;
using System.IO;

namespace AttachFileReader
{
    class Program
    {
        static void Main(string[] args)
        {
            string customParameter = args[0];
            string inputPath = args[1];
            string outputPath = args[2];

            switch (customParameter)
            {
                case "c":
                    {
                        if (!string.IsNullOrWhiteSpace(inputPath))
                        {
                            if (!string.IsNullOrWhiteSpace(outputPath))
                            {
                                try
                                {
                                    using (MemoryStream stream = new MemoryStream())
                                    {
                                        string data = File.ReadAllText(inputPath);
                                        string[] content = data.Split(new char[] { '=' });

                                        foreach (string d in content)
                                        {
                                            string unit = d;
                                            unit = d.Replace("=", "");
                                            unit += "=";

                                            Console.WriteLine(unit.Length);
                                            if (unit.Length > 1)
                                            {
                                                byte[] byteData = Convert.FromBase64String(unit);
                                                stream.Write(byteData, 0, byteData.Length);
                                            }
                                        }
                                        File.WriteAllBytes(outputPath, stream.ToArray());
                                        Console.WriteLine("Successfully saved to " + outputPath);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine("Oh snap! Something went wrong. Will this message help you?");
                                    Console.WriteLine(ex.Message);
                                }
                            }
                            else
                            {
                                Console.WriteLine("Missing parameter - index 1");
                            }
                        }
                        else
                        {
                            Console.WriteLine("Missing parameter - index 0");
                        }
                        break;
                    }
                default:
                    {
                        Console.WriteLine("Windows Phone Attached Email Reader v.1.0\nDeveloped by Den Delimarsky.");
                        break;
                    }
            }
        }
    }
}

The first argument passed to the application is c for conversion. The second argument is the path to the text file that contains the concatenated Base64 strings. This, obviously, can be automated or be done manually. The third and last argument shows the output path.

The composite string is being broken down in chunks once again, and every single one of them is decode and written to a MemoryStream instance, that works well for byte array merging, especially in a situation where I am writing those sequentially.

The byte array obtained from MemoryStream is later saved to a file.

NOTE: jpg is used as a sample extension. It can be any other extension as long as the correct file format is being sent out.

This is more of a for-fun observation than a viable option for sending files. You might want to consider these choices for applications that go public:

  • send data to a web service that forwards the file to an email address
  • use a cloud storage system and upload files there directly
  • implement the email transmission protocol independently (think about System.Net.Mail)