Blog - Microsoft .NET, ASP.NET, AJAX and more

Using Exchange Web Services 2007: The Basics

by Dave Marini 3/20/2008 8:02:00 AM
Technorati Tags: ,,

Exchange Web Services (EWS) for Microsoft Exchange 2007 provides a quick an easy way to interface with Exchange data from anywhere without having to interface with ugly COM interop objects if you're living in a managed world like C#.  Today I was curious at how to get calendar appointments from Exchange into a readable format that I can use in another application, perhaps one that synchronizes appointments from exchange with those from other calendaring applications like Google's calendaring feature.  Also, I found the documentation about EWS hard to find and not very helpful, so this post will serve as a single source of good documentation for those who are getting started with EWS.  All of the examples I give will assume you have a basic familiarity with the available operations that the web service offers you.  Here are some great links to get you up to speed:

MSDN Getting Started With EWS: A 3 part intro to Exchange 2007 Web Services.
EWS Operations: A list of the operations that can be performed on the EWS.
FindItem Example in C#: An example of querying calendar items from EWS.
Glen Scales' Blog: This guy knows Exchange inside and out.

The Exchange Web Services module works off the concept of creating a soap message to send to Exchange that will tell the server what you're looking for and then give you back a soap response. The only thing I find to be problematic in the EWS architecture is that the type objects that were used to comprise the API that C# developers will use to create these requests is almost a literal translation of the corresponding XML elements and attributes, which has a tendency of making the code bloated and a bit unintuitive. Regardless, anything is better than using the old ways of communicating with Exchange, so let's continue.

The first step in communicating with Exchange is to obtain a proxy. This is an instance of the ExchangeServicesBinding class. With Visual Studio 2005, you can generate this proxy object by adding a web reference pointed at http://<YourExchangeServer>/EWS/Services.wsdl.  As a user of Visual Studio 2008, I thought that adding a service reference would accomplish the same thing, but it turned out I was wrong. If you attempt to use a service reference to generate these types, you won't get the proxy object back to instantiate.  So, the way to do this is as follows. Right click on your project and choose "Add Service Reference". Then, when the Add Service Reference Dialog comes up, choose "Advanced". This will present you with the Service Configuration Dialog screen. From here, there will be an option on the bottom left to "Add Web Reference" and this will bring you to the screen that will ensure that you generate the appropriate proxy objects from the WSDL. See Figure 1 for some screenshots of this.

Figure 1. Adding a web reference to the exchange services.
ewsConnect

In the case you don't want to go through using a web reference, you can use the wsdl.exe tool to generate the classes that you can then reference in your project. For more information about doing this, go here.  Once you have the proxy, you will want to connect to it in order to post messages to it. The way to do this in C# is quite straightforward:

ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.Url = "http://<YourExchangeServer>/EWS/Exchange.asmx";
esb.Credentials = new NetworkCredential("username", "password", "domain"); 

Once authenticated, you will be connected to Exchange as the user in the credentials supplied.  You can also use CredentialCache.DefaultCredentials if you are making a WinForms application and want to use the credentials of the logged in user when connecting to the exchange server.  One thing to keep in mind when querying or creating items in exchange from this point is that all of these operations, with the exception of the free/busy time query, work off a single mailbox at a time, and this mailbox is initially that of the connected user.  The solution if you need to see items from other users is to impersonate the target user(s) and perform the queries.  There are 2 ways to accomplish this, one uses delegates and the other explicit impersonation.

Most operations in the Exchange Services proxy take objects that require you to specify which folders you will be working on.  For example, if I wanted to look for messages that contain the subject "happy holidays", I would probably want to look in the inbox, but also in the deleted items folder since I could have deleted the message I'm looking for.  There is a property on the folder type object called Mailbox, and this allows you to specify another person's mailbox when specifying the folder. So, assume I am connected to the Exchange Server as John Smith, but I need to find all appointments in Anne Dugger's calendar folder. Here would be the code to use delegate access to accomplish this:

FindItemType fit = new FindItemType();
fit.ItemShape = new ItemResponseShapeType { BaseShape = DefaultShapeNamesType.Default };
fit.ParentFolderIds = new DistinguishedFolderIdType[]
{ 
    new DistinguishedFolderIdType
    { 
        Mailbox = new EmailAddressType{ EmailAddress="adugger@contoso.com"}, 
        Id = DistinguishedFolderIdNameType.calendar
    }
};
fit.Traversal = ItemQueryTraversalType.Shallow;

FindItemResponseType firt = esb.FindItem(fit);

In the above example, the FindItemType is the class that we use to tell exchange to find items in a particular user's folder. Here we're specifying that we want to look in the Calendar folder of the user that has an email address of adugger@contoso.com. In order to use delegate impersonation to query Exchange, Anne Dugger would have to grant the connected user access to her calendar folder. This can be done in Outlook by going to tools->options and clicking on the Delegates tab. This tab is only visible if outlook is connected to an exchange server.  This method is useful if the functionality you want to incorporate is opt-in as users can grant a particular system account delegate access to their mailbox so that automated tasks can be performed.

The second way to accomplish querying another user's mailbox folders is explicit impersonation. This method is useful for scenarios where an Exchange Administrator wants to force one or more mailboxes to be maintainable by a system account.  The setup for this scenario is a bit more complex and is covered in detail here. Once configured, we can programmatically invoke impersonation in C# through the following means. Using the same example as above, if John Smith's account had been granted impersonation rights on Anne Dugger's mailboxes, and I wanted to find all appointments in Anne Dugger's Calendar folder, here is the C# code we would use:

ExchangeImpersonationType imp = new ExchangeImpersonationType();
imp.ConnectingSID = new ConnectingSIDType { PrimarySmtpAddress = "adugger@contoso.com" };
esb.ExchangeImpersonation = imp;

FindItemType fit = new FindItemType();
fit.ItemShape = new ItemResponseShapeType { BaseShape = DefaultShapeNamesType.Default };
fit.ParentFolderIds = new DistinguishedFolderIdType[]
{ 
     new DistinguishedFolderIdType{ Id = DistinguishedFolderIdNameType.calendar }
};
fit.Traversal = ItemQueryTraversalType.Shallow;

FindItemResponseType firt = esb.FindItem(fit);

As you can see from the code above, there is no longer a need to explicitly specify the mailbox when indicating which folders we will be searching. Instead, we tell the proxy instance that we are impersonating Anne Dugger's account by specifying her email address in the ConnectingSID property of an ExchangeImpersonationType that we bind to the proxy instance.  You can also use the principal name here if you desire, but note that this is may be different than the email address for the target user.

No matter which way you go about getting a user's information, since you can only query one user at a time, you will probably want to employ your favorite threading techniques so that you can get multiple results back in a relatively short amount of time.

Let's talk a bit about shapes.  At least for the FindItem (and perhaps GetItem) service operations, there is a property called ItemShape. This property essentially defines what we expect to come back in the response message from Exchange.  In the above example, We're telling exchange to bring back the default properties for calendar items, but what if we wanted to include a couple of non standard properties in the result? Well, we can specify these in the AdditionalProperties Property of the ItemResponseShapeType class, as in the below example:

fit.ItemShape = new ItemResponseShapeType
{ 
     BaseShape = DefaultShapeNamesType.Default, 
     AdditionalProperties = new PathToUnindexedFieldType[]
     { 
          new PathToUnindexedFieldType{ FieldURI = UnindexedFieldURIType.calendarIsMeeting },
          new PathToExceptionFieldType{ FieldURI = UnindexedFieldURIType.calendarIsRecurring }
     } 
};

The code above specifies that we want 2 custom properties to be returned, one indicating whether the appointment is a meeting and the other to indicate whether the appointment is recurring or not. We can then use these properties in the response should we have any logic for whether or not to process recurring meetings differently than other types of meeting.

Just as an aside, when searching for items in Exchange, you can specify grouping and paging options as well as restriction and sorting options.  I won't go into details about all of these, suffice it to say that the MSDN example in the link above shows all of these aspects of the FindItem operation. 

So that wraps up the basics of how to connect to and impersonate with Exchange 2007 web services.  I've only really played around with it for an hour, and it's pretty obvious that I only really used the FindItem operation since I only needed basic properties to be returned about the appointments I was searching for. For more details about the appointments (like the invitees, etc) I would have to call GetItem on each item that was returned from FindItem, passing the Item's id in as the reference. The API has the ability to do much more than search, I can programmatically create appointments, messages and contacts as well, but the underlying infrastructure is the same as it is for finding an item, so getting an understanding of it for the FindItem operation will translate to other operations as well. 

Shout it kick it on DotNetKicks.com Bookmark and Share

Comments

4/22/2008 7:37:06 AM #

Rodrigo

Brilliant, magnificent post, helpfull and very well explained. Thank you very much Dave, it helped me a lot when starting with EWS.

Rodrigo Spain

4/24/2008 8:53:42 AM #

Dennis

When I want to put a draft message in someones draft folder, I get the error:
-- The specified folder could not be found in the store.

It works with another person, I already checked the rights on there exchange mailboxes, and they are the same.

Can you tell me whats wrong, or what this error means. Is it about the mailbox folder of the user? Or the draft folder of that user?

Dennis Netherlands

8/29/2008 10:49:15 AM #

Kevin Becker

Can I get some help translating this into VB.NET?  I've tried to do it myself, but I get nothing in return so I assume that the query didn't work.  I also tried several of the free online converters with no luck.  Thanks!

Kevin Becker United States

8/29/2008 10:54:00 AM #

Damien White

Kevin,
Where are you having problems?  If I had to guess it's with the constructor initialization like:
imp.ConnectingSID = new ConnectingSIDType { PrimarySmtpAddress = "adugger@contoso.com" };

Try using:
imp.ConnectingSID = new ConnectingSIDType();
imp.PrimarySmtpAddress = "adugger@contoso.com";

instead.

Hope this helps,
-Damien

Damien White United States

9/5/2008 10:17:01 AM #

Kevin Becker

Thanks Damien.

Kevin Becker United States

1/9/2009 4:18:55 AM #

pingback

Pingback from tcup.in

TCUP - Technical Communicators and Usability Professionals » Microsoft To Enhance Entourage, SharePoint For Mac Users

tcup.in

3/26/2009 8:48:49 AM #

Iain

Did Dennis' question of 4/24/2008 8:53:42 AM ever get answered? I have a very similar issue but just with retrieving calendar items.  Works perfectly for about 50% of users but the others get the [The specified folder could not be found in the store.] issue.  I cannot spot any differences with account setup of users....

Iain United Kingdom

3/26/2009 9:12:29 AM #

Damien White

Iain -
Have a look at these comments from another blog... The first from Glen in response to the specified folder error: "... Make sure that you use the Primary SMTP Address for the account the AD UPN wont work ..." from gsexdev.blogspot.com/.../...elper-for-setting.html and the second, from Simon the orignal user who posted the question, gsexdev.blogspot.com/.../...elper-for-setting.html "... I have noticed that putting a long delay in the script (300seconds at the moment) before performing the web services stuff works fine. So it appears that there is something that is not entirely initialised when I try set the permissions via web services ..."

Hope this helps!
-Damien

Damien White United States

4/23/2009 10:00:55 AM #

Dain Muller

I am running into an issue, not searching for calendar but message, if I do the following:
     AdditionalProperties = new PathToUnindexedFieldType[]
     {
          new PathToUnindexedFieldType{ FieldURI = UnindexedFieldURIType.messageToRecipients },
          new PathToExceptionFieldType{ FieldURI = UnindexedFieldURIType.messageCcRecipients }
     }

it fails with Exchange.ResponseCodeType.ErrorInvalidPropertyForOperation.

Any idea why? I can ask for other properties, just not those.

Dain Muller United States

8/19/2009 1:22:46 PM #

Rex

I am getting the error {"The request failed with HTTP status 401: Unauthorized."} I am running this on a client machine hosting ASP.NET web app. I explicitly passed my creditials and and i have anonymous access enabled in IIS for the web service and the web site. In the web.config file I have <authentication mode="Windows"/>. I also tried with...    
<authorization>
<deny users="?"/>
</authorization>

Rex United States

8/19/2009 1:39:10 PM #

Damien White

Rex -
Are you maybe mixing credentials as shown here: social.technet.microsoft.com/.../...-e33d5891404e.  If you are setting credentials manually (as in the code from above), make sure you don't have any instances of the UseDefaultCredentials property being set.

Hope this helps,
-Damien

Damien White United States

8/19/2009 2:27:36 PM #

Rex

Damien,
The link you provided seems to be broken. The UseDefaultCredentials property is never changed. Could this be a security permissions related issue?

Rex United States

8/19/2009 2:33:04 PM #

Damien White

Rex,
Sorry, about the link, it is:
social.technet.microsoft.com/.../dd5ce2cc-c4f8-4093-8bfc-e33d5891404e

My period at the end of the sentence was integrated into the URL, and that broke it.

It could be permissions.  Are you getting this error connecting to your website, or when you actually make a call out to Exchange?

-Damien

Damien White United States

8/19/2009 2:53:05 PM #

Rex

I get the error at...
FindItemResponseType response = binding.FindItem(request);

Rex United States

9/9/2009 5:36:14 AM #

Patrick

Thanks a lot Damien.

Patrick United States

5/14/2010 6:16:27 PM #

pingback

Pingback from pholpar.wordpress.com

Accessing internal Exchange web services from an external application through ISA server using forms-based authentication « Second Life of a Hungarian SharePoint Geek

pholpar.wordpress.com

5/20/2010 2:43:39 PM #

pingback

Pingback from 466.ja3ra.com

Aftermarket W250 Dodge W200, W200 Engine Marine

466.ja3ra.com

Comments are closed