Pieter van der Westhuizen

Office 365 – Attach files from SharePoint to Outlook E-mail programmatically

In my last post, Save Outlook e-mails & attachments to SharePoint Online programmatically, I showed you how you can write a Microsoft Outlook Add-in that will save all the attachments on an e-mail to the Office 365 SharePoint Online Shared Documents library.

In today’s post I would like to show you how to do the opposite, e.g. how to attach files from SharePoint Online to Outlook e-mail messages. This could be useful if your customer has a shared library with documents they send via e-mail on a regular basis.

Let us jump straight in by creating a new ADX COM Add-in in Visual Studio 2010.

Creating a new COM Add-in project in Visual Studio 2010

Select Visual C# and Microsoft Office 2007 when prompted and finish by selecting Microsoft Outlook as the supported application. When the new project wizard is completed, make sure you have the AddinModule designer surface open and add a new ADXRibbonTab component. Change its Caption property to Office 365 and set the Ribbons property to OutlookMailCompose. We want the ribbon group to be inserted before the standard Insert Group on the Outlook mail compose form and to do this we need to set the Ribbon tabs’ IdMso property to TabNewMailMessage.

Next, add a new Ribbon Group and set its InsertBeforeIdMso property to GroupInclude. This will ensure our group is inserted before the standard Insert group. Finally add a new button to the group. Your final design should look something like the following image:

Custom Ribbon Group in the Add-in Express visual designer

Next, add a new Windows Form control to your project and change its design to resemble the following image:

Adding a new Windows Form

We’ll use this form to retrieve and display the documents in our Shared Documents library in SharePoint Online. Switch to the forms’ code view and add four string variables:

string username = string.Empty;
string password = string.Empty;
string baseUrl = string.Empty;
string siteUrl = string.Empty;

These variables will be used to store our SharePoint configuration values. We’ll load these values into the variables in the forms’ Load event.

private void frmSPAttach_Load(object sender, EventArgs e)
{
    // https://yoursite.sharepoint.com
    baseUrl = Properties.Settings.Default.SPBaseUrl;
	// /sites/OutlookTests
    siteUrl = Properties.Settings.Default.SPSiteUrl;
    username = Properties.Settings.Default.SPUserName;
    password = Properties.Settings.Default.SPPassword;
}

Next, we need to add the same files from WictorWilens’ post I’ve mentioned in my previous article. This is to access SharePoint using active authentication. You can read more about it on Wictors’ blog and Andrew Connells’ blog.

After you’ve added the ClaimsWebClient.cs, MsOnlineClaimsHelper.cs and WcfClientContracts.cs files to your project, switch back to the form we’ve created earlier.

Note. In order for the code to compile you need to add references to the following assemblies:

  • Microsoft.IdentityModel
  • Microsoft.SharePoint.Client
  • Microsoft.SharePoint.Client.Runtime
  • System.Runtime.Serialization.dll
  • System.ServiceModel.dll

Add an event handler for the Click event of the Retrieve Documents button and add the following code:

private void btnRetrieveDocs_Click(object sender, EventArgs e)
{
    Cursor = Cursors.WaitCursor;
    lvSharedDocs.Items.Clear();
 
    claimsHelper = new MsOnlineClaimsHelper(baseUrl + siteUrl, username, password);
    using (ClientContext context = new ClientContext(baseUrl + siteUrl))
    {
        context.ExecutingWebRequest += claimsHelper.clientContext_ExecutingWebRequest;
 
        List spList = context.Web.Lists.GetByTitle("Shared Documents");
        CamlQuery docQuery = new CamlQuery();
        if (String.IsNullOrEmpty(txtFilter.Text))
        {
            docQuery.ViewXml = "<View><Query></Query><RowLimit>100</RowLimit></View>";
        }
        else
        {
            docQuery.ViewXml = String.Format(
				"<View><Query><Where><Contains><FieldRef Name='FileLeafRef'/>" +
				"<Value Type='Text'>{0}</Value></Contains></Where></Query></View>",
				txtFilter.Text);
        }
        ListItemCollection spListItems = spList.GetItems(docQuery);
        context.Load(spListItems);
        context.ExecuteQuery();
 
        foreach (ListItem spItem in spListItems)
        {
            ListViewItem lvItemTitle = new ListViewItem();
            lvItemTitle.Text = spItem["FileLeafRef"].ToString();
            ListViewItem.ListViewSubItem lvSubItemSize =
				new ListViewItem.ListViewSubItem();
 
            double fileSize = 0;
            if (double.TryParse(spItem["File_x0020_Size"].ToString(), out fileSize))
            {
                fileSize = Math.Round(fileSize / 1024, 2);
            }
 
            lvItemTitle.Tag = baseUrl + spItem["FileRef"];
            lvSubItemSize.Text = fileSize + " KB";
            lvItemTitle.SubItems.Add(lvSubItemSize);
            lvSharedDocs.Items.Add(lvItemTitle);
        }
    }
    Cursor = Cursors.Arrow;
}

In the above code, we used the MsOnlineClaimsHelper to authenticate SharePoint Online and fetch the Shared Documents library. Next, we declared a CamlQuery object. This is very important even if you want to retrieve all the items in the list. CAML is an abbreviation for Collaborative Application Markup Language and is an XML-based query language used for querying SharePoint objects. To retrieve all the elements you need to set its ViewXml property to:

<View><Query></Query><RowLimit>100</RowLimit></View>

In our scenario we would like to give the user the option to filter documents based on their file names. To do this we add a Where element to the CAML xml:

<View><Query><Where><Contains><FieldRef Name='FileLeafRef'/><Value Type='Text'>YouSearchPhrase</Value></Contains></Where></Query></View>

Once we have the list loaded, we loop through each item and add its filename and size to the listview on the form. We also add the FileRef field value as a tag for each item; this is to enable us to download the file when the user clicks the “Attach Selected” button.

With the above code in place, we need to add an event handler for the “Attach Selected” button Click event and add the following:

private void btnAttach_Click(object sender, EventArgs e)
{
    Outlook.Attachments mailAttachments = null;
    try
    {
        mailAttachments = currentMailItem.Attachments;
 
        foreach (ListViewItem checkedItem in lvSharedDocs.CheckedItems)
        {
            string fileUrl = checkedItem.Tag.ToString();
            string savePath = Path.Combine(
				Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
				checkedItem.Text);
            SaveFileFromUrl(fileUrl, savePath, claimsHelper.CookieContainer);
 
            Outlook.Attachment fileAttachement = mailAttachments.Add(
				savePath, Outlook.OlAttachmentType.olByValue, 1, null);
            currentMailItem.Save();
 
            if (fileAttachement != null)
                Marshal.ReleaseComObject(fileAttachement);
        }
    }
    finally
    {
        if (mailAttachments != null)
            Marshal.ReleaseComObject(mailAttachments);
    }
    this.Dispose();
}

The code will download the checked files from SharePoint and attach them to the currently open Outlook mail item. The SaveFileFromUrl method does most of the work, and it looks like this:

private void SaveFileFromUrl(string url, string saveToPath, 
	CookieContainer cookie = null)
{
    HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(url);
    if (cookie != null)
        webRequest.CookieContainer = cookie;
    WebResponse webResponse = webRequest.GetResponse();
    Stream responseStream = webResponse.GetResponseStream();
    long contentLength = 0;
 
    byte[] responseBytes;
    if (long.TryParse(webResponse.Headers.Get("Content-Length"), out contentLength))
    {
        using (BinaryReader binReader = new BinaryReader(responseStream))
        {
            responseBytes = binReader.ReadBytes((int)contentLength);
            binReader.Close();
        }
        webResponse.Close();
 
        using (FileStream saveStream = new FileStream(saveToPath, FileMode.Create))
        {
            BinaryWriter binWriter = new BinaryWriter(saveStream);
            binWriter.Write(responseBytes);
            saveStream.Close();
            binWriter.Close();
        }
    }
}

The SaveFileFromUrl method accepts the following parameters: (a) the url of the file to download, (b) the local folder path to save it to, and (c) a CookieContainer.

Build, register and run your project. When you click on the “Add SharePoint Attachment” ribbon button, you should see the following:

Selecting the files you want to attach from the SharePoint library

When you click on the “Attach Selected” button, the selected files will be attached to the e-mail:

The selected files are attached to the e-mail

Thank you for reading. Until next time, keep coding! And remember to follow us on Twitter!

Available downloads:

These sample COM Add-ins were developed using Add-in Express for Office and .net
C# sample project

You may also be interested in:

7 Comments

  • http://0.gravatar.com/avatar/a6933089c3e87a47e790bf520a58029e?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G attach file for anonymous users says:

    hi Pieter,
    nice post..
    i wanted to know if your code implementation will work for anonymous users to create files in document library?

  • http://0.gravatar.com/avatar/e1a4c2b21a5186e0b27c1c601f418b76?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Pieter van der Westhuizen says:

    Hi There,

    Although I haven’t tried it myself yet, I’m sure it could be possible.
    It would however all depend on your permissions for the document library. It would be interesting to see if it can be done :)

  • http://1.gravatar.com/avatar/be425cd3bf5d49f0ad7881fd4f7fd29e?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G ISAAC says:

    Hi Pieter,

    I tried these code. I have encountered an error which is null pointer.
    I’m not really sure what’s wrong with it. hope u can assist me in it. thanks. :)

  • http://0.gravatar.com/avatar/e1a4c2b21a5186e0b27c1c601f418b76?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Pieter van der Westhuizen says:

    Hi Isaac,

    Where in the code do you get a null reference exception?

  • http://1.gravatar.com/avatar/be425cd3bf5d49f0ad7881fd4f7fd29e?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G ISAAC says:

    Hi Pieter,

    I’m doing the different way from you.
    I created another addin form which retrieve file from sharepoint.
    i believe it’s similar to yours. but im not using checkbox like yours,
    “string fileUrl = checkedItem.Tag.ToString();”
    i having null reference exception at this line.

    Do u mind emailing me? i will send u my code and it is easier for you to understand :)

    regards,
    isaac

  • http://0.gravatar.com/avatar/e1a4c2b21a5186e0b27c1c601f418b76?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Pieter van der Westhuizen says:

    Ok, I’ve sent you an email.

  • http://0.gravatar.com/avatar/8c91b2467759a911db863644dd52e869?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G ProCloudNor says:

    I would have paid for this solution if it was available in the Office App Store :-)

Post a comment

Have any questions? Ask us right now!