To learn more about this book, visit Microsoft Learning at...

To learn more about this book, visit Microsoft Learning at <URL looks like:
http://www.microsoft.com/MSPress/books/#####.aspx>
A.11
Extended Property Helpers
Two helper files were created to simplify dealing with the extended property proxy classes.
The first of these deals with the PathToExtendedFieldType proxy class and removes much of
the tedium when dealing with these types. Much of the class houses code for converting from
the property tag values to MapiPropertyTypeType values. Also included are several factory
methods for creating the various and sundry types of extended field uris.
// PathToExtendedFieldType.cs
//
// Copyright© 2006 David Sterling
//
using System;
using System.Collections.Generic;
using System.Text;
namespace ProxyHelpers.EWS
{
/// <summary>
/// Extension of PathToExtendedFieldType to add some
helpful overloads
/// and methods </summary>
public partial class PathToExtendedFieldType
{
private static Dictionary<int, SingleAndArrayPair>
mapping =
new Dictionary<int,
SingleAndArrayPair>();
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
2
A. 11 Extended Property Helpers
/// <summary>
/// Static constructor. Used to fill up our
dictionary
/// </summary>
static PathToExtendedFieldType()
{
mapping.Add(2,
new SingleAndArrayPair(
MapiPropertyTypeType.Short,
MapiPropertyTypeType.ShortArray));
mapping.Add(3,
new SingleAndArrayPair(
MapiPropertyTypeType.Integer,
MapiPropertyTypeType.IntegerArray));
mapping.Add(4,
new SingleAndArrayPair(
MapiPropertyTypeType.Float,
MapiPropertyTypeType.FloatArray));
mapping.Add(5,
new SingleAndArrayPair(
MapiPropertyTypeType.Double,
MapiPropertyTypeType.DoubleArray));
mapping.Add(6,
new SingleAndArrayPair(
MapiPropertyTypeType.Currency,
MapiPropertyTypeType.CurrencyArray));
mapping.Add(7,
new SingleAndArrayPair(
MapiPropertyTypeType.ApplicationTime,
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
A.11
Extended Property Helpers
MapiPropertyTypeType.ApplicationTimeArray));
mapping.Add(0xB,
new
SingleAndArrayPair(MapiPropertyTypeType.Boolean));
mapping.Add(0x14,
new SingleAndArrayPair(
MapiPropertyTypeType.Long,
MapiPropertyTypeType.LongArray));
mapping.Add(0x1E,
new SingleAndArrayPair(
MapiPropertyTypeType.String,
MapiPropertyTypeType.StringArray));
mapping.Add(0x1F,
new SingleAndArrayPair(
MapiPropertyTypeType.String,
MapiPropertyTypeType.StringArray));
mapping.Add(0x40,
new SingleAndArrayPair(
MapiPropertyTypeType.SystemTime,
MapiPropertyTypeType.SystemTimeArray));
mapping.Add(0x48,
new SingleAndArrayPair(
MapiPropertyTypeType.CLSID,
MapiPropertyTypeType.CLSIDArray));
mapping.Add(0x102,
new SingleAndArrayPair(
MapiPropertyTypeType.Binary,
MapiPropertyTypeType.BinaryArray));
}
/// <summary>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
3
4
A. 11 Extended Property Helpers
/// Returns true if the prop tag type passed in
represents an array
/// type</summary>
/// <param name="propTagType">Property tag
type</param>
/// <returns>True if array type</returns>
///
private static bool IsArrayType(ushort propTagType)
{
return (propTagType & 0xF000) !=0;
}
/// <summary>
/// Extracts the raw type from the prop tag. Will be
the same for
/// single and multivalued types </summary>
/// <param name="propTagType">Type to examine</param>
/// <returns>Raw type</returns>
///
private static ushort ExtractTypeFromArrayType(ushort
propTagType)
{
return (ushort)(propTagType & 0x0FFF);
}
/// <summary>
/// Converts from a full property tag to the
corresponding
/// MapiPropertyTypeType schema value.
/// </summary>
/// <param name="fullPropertyTag">Full proptag
including type
/// part</param>
/// <returns>MapiPropertyTypeType</returns>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
A.11
Extended Property Helpers
///
public static MapiPropertyTypeType
GetMapiPropertyType(
int
fullPropertyTag)
{
// The type is in the low word. Mask it off
//
ushort type = (ushort)(fullPropertyTag & 0xFFFF);
ushort rawType = ExtractTypeFromArrayType(type);
SingleAndArrayPair pair;
if (!mapping.TryGetValue(rawType, out pair))
{
throw new ArgumentException(
"Unsupported property type: " + type);
}
if (IsArrayType(type))
{
if (pair.ArrayValueType.HasValue)
{
return pair.ArrayValueType.Value;
}
else
{
throw new ArgumentException(
"No array type provided for type:
" + type);
}
}
else
{
return pair.SingleValueType;
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
5
6
A. 11 Extended Property Helpers
}
}
/// <summary>
/// Creates a prop tag extended field uri (proxy)
/// </summary>
/// <param name="propId">16-bit Id of property
tag</param>
/// <param name="propType">property type</param>
/// <returns>PathToExtendedFieldType proxy
object</returns>
///
public static PathToExtendedFieldType
BuildPropertyTag(
ushort propId,
MapiPropertyTypeType
propType)
{
PathToExtendedFieldType result = new
PathToExtendedFieldType();
result.PropertyTag = string.Format("0x{0:x}",
propId);
result.PropertyType = propType;
return result;
}
/// <summary>
/// Creates a GuidId extended field uri (proxy)
/// </summary>
/// <param name="guid">Guid representing the property
set</param>
/// <param name="propId">Property id of the named
property</param>
/// <param name="propType">Property type</param>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
A.11
Extended Property Helpers
/// <returns>PathToExtendedFieldType proxy
object</returns>
///
public static PathToExtendedFieldType BuildGuidId(
Guid guid,
int propId,
MapiPropertyTypeType
propType)
{
PathToExtendedFieldType result = new
PathToExtendedFieldType();
result.PropertyId = propId;
// Don’t forget to set the specified property to
true for
// optional value types!!
//
result.PropertyIdSpecified = true;
result.PropertySetId = guid.ToString("D");
result.PropertyType = propType;
return result;
}
/// <summary>
/// Builds a guid/name extended property
/// </summary>
/// <param name="guid">Property set guid</param>
/// <param name="propertyName">Property name</param>
/// <param name="propType">Property type</param>
/// <returns>Guid/Name extended property</returns>
///
public static PathToExtendedFieldType BuildGuidName(
Guid guid,
string propertyName,
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
7
8
A. 11 Extended Property Helpers
MapiPropertyTypeType
propType)
{
PathToExtendedFieldType result = new
PathToExtendedFieldType();
result.PropertySetId = guid.ToString("D");
result.PropertyName = propertyName;
result.PropertyType = propType;
return result;
}
/// <summary>
/// Nested class for holding MapiPropertyTypeType
values that are
/// related</summary>
private class SingleAndArrayPair
{
private MapiPropertyTypeType singleValue;
private MapiPropertyTypeType? arrayValue;
///
///
///
///
<summary>
Constructor
</summary>
<param name="singleValue">Type for single
valued
/// items</param>
/// <param name="arrayValue">OPTIONAL type for
multi-valued
/// items. There is no bool[] for
instance</param>
///
public SingleAndArrayPair(
MapiPropertyTypeType
singleValue,
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
A.11
Extended Property Helpers
MapiPropertyTypeType
arrayValue)
{
this.singleValue = singleValue;
this.arrayValue = arrayValue;
}
/// <summary>
/// Constructor to use for single valued items
only
/// </summary>
/// <param name="singleValue">Type for single
valued
/// items</param>
///
public SingleAndArrayPair(MapiPropertyTypeType
singleValue)
{
this.singleValue = singleValue;
this.arrayValue = null;
}
/// <summary>
/// Accessor for the single value type
/// </summary>
public MapiPropertyTypeType SingleValueType
{
get { return this.singleValue; }
}
/// <summary>
/// Accessor for the array value type
/// </summary>
public MapiPropertyTypeType? ArrayValueType
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
9
10
A. 11 Extended Property Helpers
{
get { return this.arrayValue; }
}
}
}
}
The second of these files simplifies the use of the ExtendedPropertyType proxy class. It adds
constructors for single and multi-valued properties.
using System;
using System.Collections.Generic;
using System.Text;
namespace ProxyHelpers.EWS
{
/// <summary>
/// Extension of the ExtendedPropertyType
/// </summary>
public partial class ExtendedPropertyType
{
/// <summary>
/// Constructor needed for serialization since we are
providing
/// overloaded constructors
/// </summary>
public ExtendedPropertyType(){}
/// <summary>
/// Constructor
/// </summary>
/// <param name="fieldURI">FieldURI representing
metadata about the
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
A.11
Extended Property Helpers
/// property</param>
/// <param name="value">Value for the property</param>
///
public ExtendedPropertyType(
PathToExtendedFieldType fieldURI,
string value)
{
this.ExtendedFieldURI = fieldURI;
this.Item = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="fieldURI">FieldURI representing
metadata about the
/// property</param>
/// <param name="values">PARAMS array of values for
multivalued
/// property</param>
///
public ExtendedPropertyType(
PathToExtendedFieldType fieldURI,
params string[] values)
{
this.ExtendedFieldURI = fieldURI;
NonEmptyArrayOfPropertyValuesType array = new
NonEmptyArrayOfPropertyValuesType();
array.Items = new string[values.Length];
int index = 0;
foreach (string value in values)
{
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
11
12
A. 11 Extended Property Helpers
array.Items[index++] = value;
}
this.Item = array;
}
}
}
With these two partial classes, dealing with extended properties is much easier. For instance,
creating a multi-valued extended property is reduced to two statements:
PathToExtendedFieldType metadata =
PathToExtendedFieldType.BuildGuidName(
Guid.NewGuid(),
"Foo Property",
MapiPropertyTypeType.IntegerArray);
ExtendedPropertyType prop = new ExtendedPropertyType(
metadata,
"1", "2", "3", "4", "5");
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
11
Extended Properties
With all these wonderful predefined properties available, we will never have occasion to
access anything else, right? Okay, I concede, that is unlikely. And that is precisely why
extended properties were added. So, just what is an extended property? Considering the
name, it would be reasonable to conclude that such properties are in addition to some set of
“standard” accessible properties. That is partially correct. Or it may be reasonable to deduce
that we are taking existing properties and “extending” them in some way. Maybe.
When we first starting writing specification documents for extended properties, we called the
feature “Extended MAPI Properties”. Ah, now we are getting somewhere. As the feature
progressed through docs and developement, we dropped the “MAPI” name, partially because
we didn’t want to tie the schema directly to MAPI1, and partially because MAPI sounds too
much like “happy”, and no one has ever heard of a happy property2. Regardless of the
absence of MAPI from the feature title, extended properties focus completely on accessing
native MAPI properties, and nothing more. So we can consider the feature name to mean a
way to access MAPI properties without using the properties that are explicitly defined in the
xml schema.
Let’s look at the system that sits beneath EWS so that we can appreciate the need for such
devices. Note that it is not necessary for you to understand the following background section.
1
We lost that battle too. We have a MapiPropertyTypeType enumeration in the schema.
2
To be honest, MAPI’s similarity to “happy” didn’t come up during any of the design reviews.
The MAPI title just seemed to fall out of the design.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
2
Chapter 11 Extended Properties
However, doing so will give you a better grasp of why extended properties work the way that
they do.
A little background
Messages, items, folders and the like are stored within an Exchange mailbox. A mailbox is
contained within a mailbox database. Now, if you have had any experience with databases,
you know that a table within a database has an associated schema that describes each of the
data columns and their types. Interestingly enough, whereas most relationship databases
have relatively fixed schemas, the schema within Exchange can be extended.
Each item or folder property within the database is assigned an identifier called a property tag
(proptag for short) which uniquely identifies that column within that specific database. Think
of this as the property’s name within the mailbox database. In a relational database, you ask
for properties by column name such as CUSTOMER_ID, whereas properties within the mailbox
database are accessed by proptag. A proptag is represented as an unsigned 32 bit integer.
The lower word contains the type for the property tag whereas the high word represents the
actual identifier for the property.
Figure 11-1 Property Tag Division
Property Type
As shown in Figure 11-1, the lower 16 bits of a proptag (section to the right) give us the data
type for that property. A given property can have one and only one type. EWS supported
types are listed in Table 11-1.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
Table 11-1 Supported MAPI Types
MAPI
Property
Type
Value
Extended
Property Type
.NET Type
Comments
Unspecified
0
Unsupported
Unsupported
Null
1
Null
Unsupported
Short
2
Short
Short
Int
3
Integer
Int32
Float
4
Float
float
Double
5
Double
double
Currency
6
Currency
Int64
Represents the number of
cents.
AppTime
7
ApplicationTime
double
Integer part represents
the date, fractional part
the time. Per MSDN, “This
property type is the same
as the OLE type VT_DATE
and is compatible with the
Visual Basic time
representation.”
Error
0xA
Error
Unsupported
Exists for reporting
purposes only.
Boolean
0xB
Boolean
bool
Object
0xD
Object
Unsupported
Exists for reporting
purposes only.
Exists for reporting
purposes only. Points to
an object that implements
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
3
4
Chapter 11 Extended Properties
IUnknown.
Long
0x14
Long
Int64
AnsiString
0x1E
String
string
Value is an ANSI string.
Both Unicode and ANSI
strings map to WS String
type.
String
0x1F
String
string
Value is a Unicode string
SysTime
0x40
SystemTime
DateTime
Value is an NT FILETIME.
Same as the VT_FILETIME
OLE type.
Guid
0x48
CLSID
Guid
Binary
0x102
Binary
Byte[]
Short Array
0x1002
ShortArray
Short[]
Int Array
0x1003
IntegerArray
Int[]
Float Array
0x1004
FloatArray
Float[]
Double
Array
0x1005
DoubleArray
Double[]
Currency
Array
0x1006
CurrencyArray
Int64[]
AppTime
Array
0x1007
ApplicationTimeArray Double[]
Object Array
0x100D
ObjectArray
Unsupported
Long Array
0x1014
LongArray
Int64[]
AnsiString
Array
0x101E
StringArray
String[]
Exists for reporting
purposes only.
Array of ANSI strings.
Both Unicode and ANSI
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
5
strings map to the WS
String type.
String Array
0x101F
StringArray
String[]
SysTime
Array
0x1040
SystemTimeArray
DateTime[]
Guid Array
0x1048
CLSIDArray
Guid[]
Binary Array
0x1102
BinaryArray
Byte[][]
MSDN has some good information on these types.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mapi/html/bc51730098db-4d3e-8303-557e18b5e71f.asp
So, given a proptag such as 0x0E03001E, we can break that into an identifier of 0x0E03 and a
property type of 0x001E (ANSI String). For the inquisitive, that just happens to be the text that
appears on the CC line of a message.
The 16 bit identifier range is broken into a standard range (0x0000 to 0x7FFF) and a custom
range (0x8000 – 0xFFFE). This is where things get a little bit interesting. The definition of
property tags within the standard range will be the same across mailbox databases. These
have predefined meanings and are publicly documented (well, some of them at least). Using
our example from above, 0x0E03 will refer to “Display CC” on any mailbox database that you
encounter, even across Exchange installations.
Now the custom range is a different story. As was mentioned earlier, the “schema” for a
mailbox database can be extended to include custom properties. When the store encounters
a custom property that it hasn’t seen before within a given mailbox database, it will assign
that property an unused property tag from the custom range. As a result, a given custom
property will likely be assigned different proptags across different mailbox database
instances.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
6
Chapter 11 Extended Properties
To illustrate this, let’s say that I create a message and give it a custom property called “foo”.
When I save/send the message, the mailbox database determines that it has never seen “foo”
before and extends the schema to include “foo”. During that process, it gives “foo” a unique
proptag in the custom prop range. Now, the email is shipped along the wire until it ultimately
arrives at its destination on another Exchange server. The receiving Exchange server saves the
message in the database containing the destination mailbox, and in the process of doing so,
notices that it has never seen “foo” either. It also extends its schema to include “foo” and
assigns the property an unused value in the custom prop range. What are the chances that
the two assigned proptag tags are the same? I wouldn’t count on it. With this in mind, we can
hopefully see that making assumptions about the proptag value of a custom property is not
necessarily a good thing.
Identifying extended properties
“But how do you specify a custom prop if the prop tag isn’t assigned until after the store sees
it?” I’m glad you asked. Custom properties have a secondary way to identify them. Actually,
to be more accurate, the prop tag should be considered the second way to identify the custom
property – at least when you consider the timeline of events.
A custom property, or “named” property as it is sometimes called, is uniquely identified with
either a guid + name or guid + id pair. In the case of the guid + name combination, the name is
a string and must be unique within the namespace defined by the guid. In the case of the guid
+ id combination, the id is a 32-bit integer and must be unique within the namespace defined
by the guid. A given custom property can be identified by either a guid + name or guid + id
pair. It cannot, however, be identified by both. When you create the custom property, you
must choose how it should be represented.
Going back to our example, when we create the “foo” property, what we are really doing is
creating a custom property called “foo” within some namespace. For our purposes, I am
willing to part with a precious guid or two to assist in this chapter.
MySpecialGuid = 24040483-cda4-4521-bb5f-a83fac4d19a4
YourSpecialGuid = 3cd40456-6991-4ebb-a01a-d4bc711b301f
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
7
MySpecialGuid now defines my namespace. Within this scope, I create a property with the
name of “foo”. Note that it is not sufficient to just say that I am dealing with property “foo”,
as there could also be a property “foo” within YourSpecialGuid. In fact, MySpecialGuid/foo is
completely different from YourSpecialGuid/foo. So in creating our message from above, I
actually set my custom property on the message using MySpecialGuid/foo. As suggested
above, the store looks in its internal mapping tables to see if a proptag has been assigned to
this guid + name pair. If not, it assigns one and puts the guid + name pair into its internal
mapping tables.
Very good. But how does this help us? Well, you see, although the prop tags may be different
from one mailbox database to the next, the guid + name pair will always be the same. So
rather than requesting the custom property by proptag, we request it by guid + name pair.
The store still thinks in terms of proptags, so what it does it look at its internal mapping tables
and convert the guid + name pair into its corresponding proptag. It then uses the proptag to
manipulate the property within the store.
Extended Properties in EWS
So how does this all tie into Exchange web services? Let us being by looking at how the three
types of extended properties are represented. EWS surfaces extended properties through the
PathToExtendedFieldType schema type (in types.xsd).
Listing 11-1
PathToExtendedFieldType schema type
<xs:complexType name="PathToExtendedFieldType">
<xs:complexContent>
<xs:extension base="t:BasePathToElementType">
<xs:attribute name="DistinguishedPropertySetId"
type="t:DistinguishedPropertySetType"
use="optional" />
<xs:attribute name="PropertySetId"
type="t:GuidType"
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
8
Chapter 11 Extended Properties
use="optional" />
<xs:attribute name="PropertyTag"
type="t:PropertyTagType"
use="optional" />
<xs:attribute name="PropertyName"
type="xs:string"
use="optional" />
<xs:attribute name="PropertyId" type="xs:int"
use="optional" />
<xs:attribute name="PropertyType"
type="t:MapiPropertyTypeType"
use="required" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="ExtendedFieldURI"
type="t:PathToExtendedFieldType"
substitutionGroup="t:Path" />
This single type can represent three different flavors of extended properties. We battled over
whether to break this into three different extended field uri types or whether we should keep
this as a single type. For better or worse, we kept the single type. The attribute combination
determines the flavor of extended property. Notice in Listing 11-1, the only required attribute
is the property type.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
9
Property Type
Regardless of flavor, all PathToExtendedFieldType instances must have the PropertyType
attribute set. The property type attribute dictates the data type that values of this custom
property have. As noted in the schema, this is a “MapiPropertyTypeType” and can contain any
of the supported values shown in Table 11-1. Rendered in xml, this would look as follows:
<t:ExtendedFieldURI PropertyType="Integer" …more…/>
If you are using the proxy classes, you would do the following:
PathToExtendedFieldType result = new
PathToExtendedFieldType();
// set the property type
//
myCustomProp.PropertyType = MapiPropertyTypeType.Integer;
Notice that the PropertyType property does not have a PropertyTypeSpecified associated
property. Can you guess why? Because the schema requires that property to be there
(use=”required”), and therefore, we do not need an extra flag to indicate its existence.
Property Tags
Although we have mapped many of the standard properties in our schema, there are still a
number of standard properties that can only be accessed by property tag. You express a
property tag extended property by a combination of the PropertyTag attribute and the
required PropertyType attribute. No other attributes are permitted.
<t:ExtendedFieldURI PropertyTag="0x1234"
PropertyType="String"/>
One thing that should be noticeably different between the MAPI and EWS representation or
property tags is that in EWS, the identifier and type are broken out into separate values.
There are two main reasons for this. First, it is much easier to see what type a given property
is referring to when dealing with enumeration values rather than hex identifiers. Second, by
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
10
Chapter 11 Extended Properties
restricting the property type to an enumeration and that is schema validated, we remove the
possibility of encountering a garbage type within a PathToExtendedFieldType instance.
What this does mean, however, is that if you have a MAPI prop tag in hand, you will need to
determine which property type the least significant word is referring to. To assist in this, we
can use a simple routine 3such as the one below for converting between MAPI proptag values
and EWS property types.
Listing 11-2
Converting a property tag into a MapiPropertyTypeType value
/// <summary>
/// Converts from a full property tag to the corresponding
/// MapiPropertyTypeType schema value.</summary>
/// <param name="fullPropertyTag">Full proptag including
type part</param>
/// <returns>MapiPropertyTypeType</returns>
///
public static MapiPropertyTypeType GetMapiPropertyType(int
fullPropertyTag)
{
// The type is in the low word. Mask it off
//
short type = (short)(fullPropertyTag & 0xFFFF);
switch (type)
{
case 2: return MapiPropertyTypeType.Short;
case 3: return MapiPropertyTypeType.Integer;
3
Appendix A-11 provides an extension to the PathToUnindexedFieldType class that performs
this work and more.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
11
case 4: return MapiPropertyTypeType.Float;
case 0x001E: return MapiPropertyTypeType.String;
// The rest are left as an exercise for the reader
:)
//
default:
throw new ArgumentException("Unsupported
property type: " +
type);
}
}
Earlier in the chapter we discussed how proptag values for custom properties are not
guaranteed to be the same across mailbox databases. As a result of this, EWS does not
support referencing custom properties (0x8000 – 0xFFFF) by their proptag value. You must
reference such properties by their guid/name or guid/id pair. Of course, all reserved
properties (<0x8000) can be accessed via proptag.
Custom properties by name
Named custom properties deal with several different attributes in PathToExtendedFieldType
in addition to the PropertyType attribute. The first of these defines the namespace or scope
for your property name. Even though we are talking about the custom range here, there are
some “standard” namespaces that are exposed through the DistinguishedPropertySetType
enumeration. Internally, these map to their respective guid namespace values. They were
provided simply to reduce eye strain – I have never really enjoyed staring at guids, although I
am quite thankful for them. To use these well-known namespaces, you must set the
DistinguishedPropertySetId attribute.
<t:ExtendedFieldURI
DistinguishedPropertySetId="PublicStrings" ..more../>
And in proxy code…
PathToExtendedFieldType fieldUri = new
PathToExtendedFieldType();
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
12
Chapter 11 Extended Properties
fieldUri.DistinguishedPropertySetId =
DistinguishedPropertySetType.PublicStrings;
// Don't forget to set the specified property for optional
value type
// properties
//
fieldUri.DistinguishedPropertySetIdSpecified = true;
For all other namespaces, or for those who really like guids, the PropertySetId attribute is
available. This attribute expects a guid without the enclosing braces. Capitalization of the
guid does not matter. Here we will use the guid format of the PublicStrings4 well known
namespace.
<t:ExtendedFieldURI PropertySetId="00020329-0000-0000C000-000000000046" … more… />
For the proxy class, the PropertySetId is a string rather than a guid. Since strings are reference
types, there is no need for the *Specified property, even though the attribute is optional in
the schema. Reference types use null to indicate that no value has been set.
PathToExtendedFieldType fieldUri = new PathToExtendedFieldType();
fieldUri.PropertySetId = "00020329-0000-0000-C000-000000000046";
Of course it wouldn’t be too useful if we didn’t have a way to dictate which property we were
referring to within our namespace. That is the job of the aptly named PropertyName
attribute. This attribute takes a case sensitive string.
PathToExtendedFieldType fieldUri = new PathToExtendedFieldType();
fieldUri.PropertySetId = "00020329-0000-0000-C000-000000000046";
fieldUri.PropertyName = "Foo";
fieldUri.PropertyType = MapiPropertyTypeType.Integer;
4
Both extended field uris will therefore point to the same property.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
13
Custom properties by Id
Just as with guid/name custom properties, we use the property set (or distinguished property
set) to identify our scope. The difference is that instead of specifying the PropertyName
attribute, we use the PropertyId attribute. The PropertyId attribute is an integer.
<t:ExtendedFieldURI
PropertySetId=”00020329-0000-0000-C000-000000000046”
PropertyId=”2”
PropertyType=”CLSIDArray”/>
Since PropertyId is an optional value type, the proxy surfaces the PropertyIdSpecified boolean
property that must be set to true if you are going to supply a value for the property id.
PathToExtendedFieldType fieldUri = new PathToExtendedFieldType();
fieldUri.PropertySetId = "00020329-0000-0000-C000-000000000046";
fieldUri.PropertyId = 2;
fieldUri.PropertyIdSpecified = true;
fieldUri.PropertyType = MapiPropertyTypeType.Integer;
When working with the proxy classes, I found it extremely helpful to add a couple of
constructors or factory methods to this class that would set the various properties correctly. I
have added these in Listing 11-3.
Listing 11-3
Factory methods for PathToExtendedFieldType
/// <summary>
/// Creates a prop tag extended field uri (proxy)
/// </summary>
/// <param name="propId">16-bit Id of property tag</param>
/// <param name="propType">property type</param>
/// <returns>PathToExtendedFieldType proxy
object</returns>
///
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
14
Chapter 11 Extended Properties
public static PathToExtendedFieldType BuildPropertyTag(
ushort propId,
MapiPropertyTypeType propType)
{
PathToExtendedFieldType result = new
PathToExtendedFieldType();
result.PropertyTag = string.Format("0x{0:x}", propId);
result.PropertyType = propType;
return result;
}
/// <summary>
/// Creates a GuidId extended field uri (proxy)
/// </summary>
/// <param name="guid">Guid representing the property
set</param>
/// <param name="propId">Property id of the named
property</param>
/// <param name="propType">Property type</param>
/// <returns>PathToExtendedFieldType proxy
object</returns>
///
public static PathToExtendedFieldType BuildGuidId(
Guid guid,
int propId,
MapiPropertyTypeType propType)
{
PathToExtendedFieldType result = new
PathToExtendedFieldType();
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
15
result.PropertyId = propId;
// Don’t forget to set the specified property to true
for optional value
// types!!
//
result.PropertyIdSpecified = true;
result.PropertySetId = guid.ToString("D");
result.PropertyType = propType;
return result;
}
Metadata and data
While schema-defined properties will always have their data represented in first class form,
extended properties have no such first-class representation. For instance, the schema-defined
unindexed field type “item:Subject” will always expose its data as a child of an Item element
(or derivative).
<!--Subject is declared in schema as a child of Item -->
<t:Item>
<t:Subject>Foo</t:Subject>
</t:Item>
You will certainly see the metadata representation of such unindexed properties <t:FieldURI
FieldURI=”item:Subject”/>, but never directly in connection with the data values.
In contrast, when we come to the actual data values for an extended property, we will
encounter both the metadata and the data instance representations. Here we must represent
both who (metadata) we are talking about and what (data) its value is.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
16
Chapter 11 Extended Properties
Figure 11-2 Extended Property Structure
It follows that the property type indicated within the metadata must agree with the type of
the data. So the following would result in an error:
<!-- This won’t work since the types don’t match -->
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag=”0x1234”
PropertyType=”Integer”/>
<t:Value>This is a string</t:Value>
</t:ExtendedProperty>
That brings up an interesting question. Since XML is all string based, how do you encode the
various types within the value element?
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
17
Table 11-2 String representation of property types
MapiPropertyTypeType Comments
Example
ApplicationTime
Write out as a double
1234.12
Binary
Base-64 encoded string. Use
System.Convert.ToBase64String()
BAUGBw==
Boolean
For False use false or 0
true
For True use true or 1
CLSID
A guid string without the enclosing
brackets. If using a Guid type, use
myGuid.ToString(“D”)
24040483-cda44521-bb5fa83fac4d19a4
Currency
1234
Double
1234.45
Float
1234.45
Integer
1234
Long
1234
Short
1234
SystemTime
String
Internally, we represent this as a
DateTime and parse using
DateTime.TryParse. We assume UTC if
no time zone is specified.
5/6/2005 8:30am
“foo”
Listing 11-4 shows an example of using the proxy classes to create an extended property with
a SystemTime type.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
18
Chapter 11 Extended Properties
Listing 11-4
// Set up the metadata (which property we are referring to)
//
PathToExtendedFieldType metadata = new PathToExtendedFieldType();
metadata.PropertyTag = “0x1234”;
metadata.PropertyType = MapiPropertyTypeType.SystemTime;
// Now create the container and set the value
//
ExtendedPropertyType extendedProperty = new ExtendedPropertyType()
extendedProperty.ExtendedFieldURI = metadata;
// For single valued extended properties, Item must be a string
//
extendedProperty.Item = DateTime.Now.ToString();
Multi-valued properties
Extended properties can also support multi-valued collections. This is done by modifying the
data section of an extended property slightly as shown in Listing 11-5.
Listing 11-5
Multi-valued extended property
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag=”0x1235”
PropertyType=”IntegerArray”/>
<t:Values>
<t:Value>1</t:Value>
<t:Value>2</t:Value>
<t:Value>3</t:Value>
</t:Values>
</t:ExtendedProperty>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
19
Now, let’s do the same thing with the proxy classes.
// Set up the metadata (which property we are referring to)
PathToExtendedFieldType metadata = new PathToExtendedFieldType();
metadata.PropertyTag = “0x1235”;
metadata.PropertyType = MapiPropertyTypeType.IntegerArray;
// Now create the container and set the value
//
ExtendedPropertyType extendedProperty = new ExtendedPropertyType()
extendedProperty.ExtendedFieldURI = extendedFieldUri;
// For multi-valued properties, Item is a
NonEmptyArrayOfPropertyValuesType
//
NonEmptyArrayOfPropertyValuesType arrayValues = new
NonEmptyArrayOfPropertyValuesType();
arrayValues.Items = new string[3];
for (int index = 1; index <= 3; index++)
{
arrayValues.Items[index-1] = index
}
extendedProperty.Item = arrayValues;
Using Extended Properties
Overriding extended property types
Let’s say we already have extended property X in the mailbox database as an integer. What
happens if you create a new item and set property X as a string? Good question. If you
guessed that it averaged the two types and turned the result into a Guid, you are incorrect. If,
however, you guessed that it overwrote the first value, you would be correct. If you then try
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
20
Chapter 11 Extended Properties
to go back and retrieve the property using the original type, it will no longer be there. The
new version (with the new type) overwrote the old version.
This redefinition is specific to the item in question. So let’s say that as in the above example,
we have extended property X as an integer and we set that on message A and on message B.
If we then go and update message B so that property X is now a string, that does not affect
property X on message A. Property X will remain an integer on message A. Property X on
message A and the updated property X on message B are now two different properties and
have two different property tags.
Specifying extended properties in shapes
Let’s assume that we have some arbitrary object within the store and we want to retrieve it
along with several of its extended properties. We need to indicate our need for these
extended properties using the AdditionalProperties element for the item or folder shape in
question.
First, let’s get something in the mailbox that has extended properties using our friend
CreateItem from chapter 4. Since we are setting the values for these properties, we need to
use the ExtendedProperty container element containing both the metadata (the property in
question) and the data that we wish to set. Let’s set two different properties just for fun.
Listing 11-6
Creating an item with two extended properties
<CreateItem xmlns=".../messages" xmlns:t=".../types"
MessageDisposition="SaveOnly">
<Items>
<t:Message>
<t:Subject>Test27</t:Subject>
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="0x1234"
PropertyType="StringArray"/>
<t:Values>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
21
<t:Value>Fee</t:Value>
<t:Value>Fi</t:Value>
</t:Values>
</t:ExtendedProperty>
<t:ExtendedProperty>
<t:ExtendedFieldURI
DistinguishedPropertySetId="PublicStrings"
PropertyName="ShoeSize"
PropertyType="Float"/>
<t:Value>12</t:Value>
</t:ExtendedProperty>
</t:Message>
</Items>
</CreateItem>
Notice in Listing 11-6 we were able to specify a plurality of ExtendedProperty elements for our
message. After successful creation, we should be able to retrieve the item with a GetItem call.
GetItem will return the properties that are applicable to the message we just created, but that
certainly won’t include our custom properties. So how do we indicate our desire to retrieve
those properties? We need to explicitly ask for them within the AdditionalProperties child
element of the item response shape as shown in Listing 11-7.
Listing 11-7
Retrieving a message with extended properties
<GetItem xmlns=".../messages" xmlns:t=".../types">
<ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
<t:AdditionalProperties>
<t:ExtendedFieldURI PropertyTag="0x1234"
PropertyType="StringArray"/>
<t:ExtendedFieldURI
DistinguishedPropertySetId="PublicStrings"
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
22
Chapter 11 Extended Properties
PropertyName="ShoeSize"
PropertyType="Float"/>
</t:AdditionalProperties>
</ItemShape>
<ItemIds>
<t:ItemId Id="AAAtAEFkbWluaXN…=" ChangeKey="CQAAs8A6QI2x…"
/>
</ItemIds>
</GetItem>
In the AdditionalProperties element of our GetItem call, we only use the metadata format of
the properties that we wish to request. That should make sense as we are requesting the data
and therefore would have no values to put there.
And the response shows us that things went as planned….
<GetItemResponse xmlns:m=".../messages" xmlns:t=".../types"
xmlns=".../messages">
<m:ResponseMessages>
<m:GetItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Items>
<t:Message>
<t:ItemId Id="…" ChangeKey="…"/>
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="0x1234"
PropertyType="StringArray"/>
<t:Values>
<t:Value>Fee</t:Value>
<t:Value>Fi</t:Value>
</t:Values>
</t:ExtendedProperty>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
23
<t:ExtendedProperty>
<t:ExtendedFieldURI
DistinguishedPropertySetId="PublicStrings"
PropertyName="ShoeSize"
PropertyType="Float"/>
<t:Value>12</t:Value>
</t:ExtendedProperty>
</t:Message>
</m:Items>
</m:GetItemResponseMessage>
</m:ResponseMessages>
</GetItemResponse>
To specify extended properties within the response shape of a proxy request, use the
ItemResponseShapeType proxy class as shown in Listing 11-8.
Listing 11-8
Requesting extended properties using the proxy
// Create our response shape and set the base shape type
//
ItemResponseShapeType responseShape = new
ItemResponseShapeType();
responseShape.BaseShape = DefaultShapeNamesType.IdOnly;
// Create our additional property array
//
responseShape.AdditionalProperties = new
BasePathToElementType[2];
// Build our two extended field uris.
//
PathToExtendedFieldType extendedProp1 = new
PathToExtendedFieldType();
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
24
Chapter 11 Extended Properties
extendedProp1.PropertyTag = "0x1234";
extendedProp1.PropertyType =
MapiPropertyTypeType.StringArray;
PathToExtendedFieldType extendedProp2 = new
PathToExtendedFieldType();
extendedProp2.DistinguishedPropertySetId =
DistinguishedPropertySetType.PublicStrings;
extendedProp2.DistinguishedPropertySetIdSpecified = true;
extendedProp2.PropertyName = "ShoeSize";
extendedProp2.PropertyType = MapiPropertyTypeType.Float;
// Set the additional properties on the response shape
//
responseShape.AdditionalProperties[0] = extendedProp1;
responseShape.AdditionalProperties[1] = extendedProp2;
Updating extended properties
Updating extended properties on an item follows the same pattern as updating any other
property. Simply specify the property that you wish to change using the metadata format and
then embed the extended property (both metadata and data) as a child element of the item in
the update call. This is shown in Listing 11-9.
Listing 11-9
Updating an item with extended properties
<UpdateItem MessageDisposition="SaveOnly"
ConflictResolution="AutoResolve"
xmlns=".../messages" xmlns:t=".../types">
<ItemChanges>
<t:ItemChange>
<t:ItemId Id="AAAtAEFkbWluaXN…"
ChangeKey="CQAAABYAA…"/>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
25
<t:Updates>
<t:SetItemField>
<t:ExtendedFieldURI PropertyTag="0x1234"
PropertyType="StringArray"/>
<t:Message>
<t:ExtendedProperty>
<t:ExtendedFieldURI
PropertyTag="0x1234"
PropertyType="StringArray"/>
<t:Values>
<t:Value>Foe</t:Value>
<t:Value>Fum</t:Value>
</t:Values>
</t:ExtendedProperty>
</t:Message>
</t:SetItemField>
</t:Updates>
</t:ItemChange>
</ItemChanges>
</UpdateItem>
NOTE: Extended properties are not supported for append operations. As such trying to
use an AppendToItemField element within a UpdateItem call on an extended property will
result in an error. To “mimic” appends, first fetch the data, append the new data and then
call UpdateItem with a SetItemField action.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
26
Chapter 11 Extended Properties
Odds and Ends
Overriding EWS business logic
The EWS business logic layer tries to make sense of MAPI by restricting which properties you
can set on a given item type. For instance, most people don’t need to get/set a start date on a
message. That is typically used only by calendar items and tasks. What if you do really need
to set the start date on a message? You can look up the MAPI information for the property in
question and access it using the extended property syntax.
There are other object-level validation steps that are performed before save time that you
can’t get around. For instance, a calendar item must have both a start date and an end date,
and the start date must be less than or equal to the end date. It will do you no good to try to
set the start date to be greater than the end date via extended properties. For the most part,
however, the field is quite open. I would recommend, however, that you have a valid business
reason for trying to get around the EWS business logic that was put in place.
MAPI versus Calculated properties
A large portion of the properties defined within the EWS schema are backed by MAPI
properties. There are some properties, however, that are calculated and therefore have no
direct underlying MAPI property. In some cases, these calculations are quite simple, but
others can be quite complex. What does this mean to you? Well, given that you can override
the EWS business logic for MAPI properties, there will come a time when you want to find the
MAPI property identifiers for one of these calculated properties. I speak with a clear
conscience – they don’t exist. These properties are noted in the appendix.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
27
Make my life easier
To make our lives a little easier, we can extend the partial proxy classes to move much of the
boilerplate code into the proxy classes themselves. Refer to appendix A.11 for more details.
There are a bunch of MAPI based applications out there that will potentially be migrated to
EWS. So, what if you have a MAPI PR_ENTRYID and want to feed this to Exchange Web
Services? I could imagine that this would occur in several cases.
-
You may have a local store of items that you would like to access, but all you have is
the lowly PR_ENTRYID since the local store was created using a legacy application.
-
You may be upgrading a legacy app piece by piece and have some legacy components
that hand your code PR_ENTRYIDs and you need to send these off to your shiny new
web service code.
-
You are irritated that nothing in the web services schema is prefixed with PR_ and are
intent on bucking the system.
The good news is that this can be done. The bad news is that the performance is quite lower
than accessing the item using the web services ItemId. With that in mind, let’s look at how we
would do this. Note that the following uses concepts discussed in chapter 12.
Grabbing an EntryID using Outlook Spy
Now in the scenarios above, you will already have the PR_ENTRYID value. But for this
example, I do not, so let’s grab one from Outlook Spy and play with it.
I would highly recommend that you purchase a copy of Outlook Spy. It is a great tool for
digging around in your mailbox and understanding how things work. Outlook Spy works as an
add-in for Microsoft Outlook. You can find Outlook Spy on the web at
http://www.dimastr.com/outspy.
Using Outlook Spy, we can determine the MAPI property information for PR_ENTRYID.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
28
Chapter 11 Extended Properties
Figure 11-3 PR_ENTRYID in Outlook Spy
Using the binary HexView editor in Outlook Spy (the little folder icon by the value text box), I
can save the binary data for the entry id out to a file (File |Save).
Figure 11-4 Outlook Spy HexView viewing the PR_ENTRYID
Now that we have an entry id to play with, let’s continue. Here is the general idea behind
what we are going to do. First, we can use extended properties to reference the PR_ENTRYID
extended MAPI property. Outlook Spy shows us that the property tag for PR_ENTRYID is
0x0FFF0102and its type is a byte[] (PT_BINARY), so all we are concerned with is the most
significant word, which is the left four hex digits right after 0x (0FFF). We can use this in our
ExtendedFieldURI to reference the property.
With this information, we can then perform a shallow traversal item query to look for the item
that has the entry id in question. We perform this search using the FindItem web method.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
29
All binary properties must be base-64 encoded when dealing with Exchange Web Services.
The System.Convert class surfaces the ToBase64String and FromBase64String methods which
will serve us quite nicely. So let’s take the binary entry Id that we saved out to a file, read it in
and convert it to a base64 string.
using (FileStream fs = File.OpenRead(@"c:\MyEntryId.dat"))
{
BinaryReader reader = new BinaryReader(fs);
byte[] bytes = reader.ReadBytes((int)fs.Length);
string base64 = System.Convert.ToBase64String(bytes);
}
When I run the above code, I get the following base64 encoded entry id:
AAAAAIUnJ7skJWxMk4SkP5mmyOgHAIeKIfEv1k9KqJx6faPnw54AAACiQdwAANw+LZ+kl0
NBpOrzVAeB39sAO7niICgAAA==
Now we have our EntryId and can continue where we left off.
Taking the base64 representation our our EntryID, we can build a restriction (query) as shown
in Listing 11-10.
Listing 11-10
Finding an item by PR_ENTRYID
<FindItem xmlns=".../messages" xmlns:t=".../types"
Traversal="Shallow">
<ItemShape>
<t:BaseShape>Default</t:BaseShape>
</ItemShape>
<Restriction>
<t:IsEqualTo>
<t:ExtendedFieldURI PropertyTag="0x0FFF"
PropertyType="Binary"/>
<t:FieldURIOrConstant>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
30
Chapter 11 Extended Properties
<t:Constant
Value="AAAAAIUnJ7skJWxMk4SkP5mmyOgHAIeKIfEv1k9KqJx6faPnw54
AAACiQdwAANw+LZ+kl0NBpOrzVAeB39sAO7niICgAAA=="/>
</t:FieldURIOrConstant>
</t:IsEqualTo>
</Restriction>
<ParentFolderIds>
<t:DistinguishedFolderId Id="inbox"/>
</ParentFolderIds>
</FindItem>
The query in Listing 11-10 says, “Give me all the items (<FindItem>) that are direct children of
the inbox (<ParentFolderIds>) that have this extended property set to this binary value
(<Restriction>)” Does it work? Let’s see…
<FindItemResponse xmlns:m=".../messages" xmlns:t=".../types"
xmlns=".../messages">
<m:ResponseMessages>
<m:FindItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:RootFolder TotalItemsInView="1"
IncludesLastItemInRange="true">
<t:Items>
<t:Message>
<t:ItemId Id="AAAeAGRdnX..."
ChangeKey="CQAAABYAA..."/>
<t:Subject>See if you can find me!</t:Subject>
<t:Sensitivity>Normal</t:Sensitivity>
<t:Size>2598</t:Size>
<t:DateTimeSent>2006-0921T17:13:19Z</t:DateTimeSent>
<t:DateTimeCreated>2006-0921T17:13:35Z</t:DateTimeCreated>
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.
Chapter 11
Extended Properties
31
<t:HasAttachments>false</t:HasAttachments>
<t:From>
<t:Mailbox>
<t:Name>David Sterling</t:Name>
</t:Mailbox>
</t:From>
<t:IsRead>false</t:IsRead>
</t:Message>
</t:Items>
</m:RootFolder>
</m:FindItemResponseMessage>
</m:ResponseMessages>
</FindItemResponse>
Now, we can extract the ItemId from this response and we have our ItemId identifier. The
above example has a large performance impact on the server, but it does get you there.
Summary
Ah, yes, extended properties. We now know what extended properties are, their various
flavors, and how to identify them in both xml and in proxy code. We covered the difference
between standard props and named props and discussed why you cannot identify custom
properties by property tag. The difference between the metadata and data representations of
extended properties was discussed. Extended properties are a valuable tool in any EWS
developer’s toolbox, and a good understanding of them will provide you with hours of fun.
DRAFT CONTENT: This content is excerpted from an upcoming title from Microsoft Learning.
This content is not complete and is subject to change prior to the release of the final, fully edited, document.