InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

Searching a meeting with a specific UID using Exchange Web Services 2007

For most items, the item id (which is based on the MAPI entry id) is a good candidate for a long-term identifier. As long as the item is not moved between folders, it remains stable. At least for most items. This is, however, not true for meetings. Exchange recreates meeting items under certain circumstances and the meeting items loose their original entry id, thus the item id is also modified. But a meeting has a different property which uniquely identifies it, even across mailbox boundaries: The UID property. This property is available as a first-class property since Exchange 2007 Service Pack 1. It is a string that looks like this:

   1: 040000008200E00074C5B7101A82E008000000007069352667BBC9010000000000000000100000000C74ABD802575A41BC09B0E12352657B

Since this property is not an Item Id, the GetItem operation cannot be used to bind to a meeting element directly. Sadly, the FindItem operation doesn’t work either, because the Exchange WebServices do not accept a restriction that is based on the UID property.

Luckily, the UID can be read and searched via a MAPI property: The GlobalObjectId. This is a binary property and when fetched looks like this:

   1: BAAAAIIA4AB0xbcQGoLgCAAAAABwaTUmZ7vJAQAAAAAAAAAAEAAAAAx0q9gCV1pBvAmw4SNSZXs=

The value is the equivalent to the UID displayed above, but in a different format. The UID is formatted as a hex-string and the GlobalObjectId is displayes as a Base64 string. Using the following small function, the first representation can be converted into the latter:

   1: private static string GetObjectIdStringFromUid(string id)
   2: {
   3:     var buffer = new byte[id.Length/2];
   4:     for (int i = 0; i < id.Length/2; i++)
   5:     {
   6:         var hexValue = byte.Parse(id.Substring(i*2, 2), NumberStyles.AllowHexSpecifier);
   7:         buffer[i] = hexValue;
   8:     }
   9:     return Convert.ToBase64String(buffer);
  10: }

The value returned from the GetObjectIdStringFromUid can now be used to execute a FindItem operation, specifying a restriction on the GlobalObjectId property. Here is the whole function:

   1: static void Main(string[] args)
   2: {
   3:     ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
   4:  
   5:     const string id = "040000008200E00074C5B7101A82E008000000007069352667BBC9010000000000000000100000000C74ABD802575A41BC09B0E12352657B";
   6:  
   7:     string bufferString = GetObjectIdStringFromUid(id);
   8:  
   9:     var service = new ExchangeServiceBinding
  10:         {
  11:           Url = "https://w2k3x64/ews/exchange.asmx",
  12:           Credentials = new NetworkCredential("bob", "Password!", "contoso"),
  13:           RequestServerVersionValue = new RequestServerVersion { Version = ExchangeVersionType.Exchange2007_SP1}
  14:         };
  15:  
  16:     var response = service.FindItem(new FindItemType
  17:         {
  18:             ParentFolderIds = new[] { new DistinguishedFolderIdType { Id = DistinguishedFolderIdNameType.calendar } },
  19:             ItemShape = new ItemResponseShapeType
  20:                 {
  21:                     BaseShape = DefaultShapeNamesType.AllProperties,
  22:                 },
  23:             Restriction = new RestrictionType {
  24:                 Item = new IsEqualToType
  25:                     {
  26:                       Item = new PathToExtendedFieldType
  27:                         {
  28:                            DistinguishedPropertySetId = DistinguishedPropertySetType.Meeting,
  29:                            DistinguishedPropertySetIdSpecified = true,
  30:                            PropertyId = 0x03,
  31:                            PropertyIdSpecified = true,
  32:                            PropertyType = MapiPropertyTypeType.Binary
  33:                         },
  34:                       FieldURIOrConstant = new FieldURIOrConstantType
  35:                         {
  36:                           Item = new ConstantValueType
  37:                           {
  38:                               Value = bufferString
  39:                           }
  40:                         }
  41:                     }
  42:                 }
  43:         });
  44:     var messageType = ((FindItemResponseMessageType) response.ResponseMessages.Items[0]);
  45:     Console.Out.WriteLine("messageType.ResponseClass = {0}", messageType.ResponseClass);
  46:  
  47:     if (messageType.ResponseClass != ResponseClassType.Success)
  48:     {
  49:         Console.Out.WriteLine(messageType.MessageText);
  50:         return;
  51:     }
  52:     var returnedItems = messageType.RootFolder.Item as ArrayOfRealItemsType;
  53:  
  54:     if (returnedItems == null || returnedItems.Items == null)
  55:     {
  56:         Console.Out.WriteLine("Nothing found.");
  57:         return;
  58:     }
  59:  
  60:     foreach (CalendarItemType item in returnedItems.Items)
  61:     {
  62:         Console.Out.WriteLine("item.Subject = {0}", item.Subject);
  63:         Console.Out.WriteLine("item.UID = {0}", item.UID);
  64:     }
  65:     Console.ReadLine();
  66: }

Posted by Henning Krause on Monday, April 13, 2009 11:45 PM, last modified on Sunday, May 8, 2011 8:37 AM
Permalink | Post RSSRSS comment feed