Friday, March 18, 2005

Outlook .NET add-ins connected to Exchange services

In some solutions we implement Outlook COM add-ins with .NET using the templates provided by Microsoft on MSDN. We typically add some menus, toolbars and dialog boxes that allow the users to manage stuff in their mailboxes or public folders, using client-side CDO. To avoid the Outlook security popup we utilize the Redemption component, which is widely recommended on the 'net for this purpose.

In addition, we sometimes need to access extended MAPI properties that are not exposed in CDO, and Redemption gives us easy access to these properties and other stuff that is not supported by client-side CDO. Redemption is a component that is really useful, so is the OutlookSpy tool. Recommended!

In an earlier posting, I wrote about how we prefer to implement code for managing ActiveDirectory and Exchange and its mailbox and public folder stores as server-side services. These services uses e.g. CDOEX and CDOEXM to perform the logic provided by our services.

One of our service methods is to copy a mail message from a mailbox store to a public folder store in response to an action initiated by an Outlook user through our COM add-in. This is not easy to do using CDO because it involves separate stores and also involves having sufficient permissions on the stores. This operation is, however, quite easy to implement using Exchange OleDB on the server-side and impersonating a specific identity that has the required rights. The ExOleDB methods for operating on different items in the store requires either the HTTP: or the File: URL Scheme, of which Microsoft recommends the HTTP: scheme.

The HTTP: URL scheme for mail messages uses the .EML name of the message item, and this URL is easy to see when using Outlook Web Access (OWA). This URL is available only through extended MAPI, thus we use Redemption to get it from the message item:

//get MAPI IMessage propery["PR_URL_COMP_NAME"]
int PR_URL_COMP_NAME = 0x10F3001E;
Redemption.SafeMailItem message = new Redemption.SafeMailItemClass();
message.Item = this._mailItem;
string url = message.get_Fields(PR_URL_COMP_NAME).ToString();

The string returned by Redemption is just the 'name' component of the URL (i.e. the message item subject). It must be encoded before passing it to the Exchange service for processing by ExOleDB:

//removed code from sample:
//
handle that Redemption returns '?' for '/'
//and other quircks with the char encoding

//encode message item URL
url = url.Replace("/", "_xF8FF_");
url = Util.MailUrlEncode(url);
//clean up
Redemption.MAPIUtils util = new Redemption.MAPIUtilsClass();
util.Cleanup();

Note that not all characters in the URL needs to be encoded, and I have some small utility functions to handle the encoding:

public static string MailUrlEncode(string input)
{
string output="";
char[] uniChars = input.ToCharArray();
foreach(char uniChar in uniChars)
{
if(uniChar <= 'z') output += uniChar.ToString(); else
output += Util.ExchangeEncode(uniChar.ToString());
}
return output;
}

public static string ExchangeEncode(string uniChar)
{
string output="";
byte[] encoded = System.Text.Encoding.UTF8.GetBytes(uniChar);
foreach(byte utf8Char in encoded)
{
output += "%" + utf8Char.ToString("X2");
}
return output;
}

Finally, the URL must be prepended with the path to the message item store (mailbox or public folder) before it becomes a complete URL that ExOleDB can operate on.

The server-side ExOleDB code for copying the message item and its attachment is not included here. I might publish a posting on this later on.

1 comment:

Shyam said...

I am using the same code and in the line where there is get_Fields it is giving me "object reference not set to an instance.." error. what could be the reason for this?