Tuesday, November 20, 2007

Calling Exchange COM objects from a .NET web service

Suppose we have the following sample code (assuming you're using System.DirectoryServices and your project has references to System.DirectoryServices and CDOEXM):

string ldapPath="ldap://{DN to user object}";
string homeMDBUrl = "{DN to private exchange store}";
using(DirectoryEntry de = new DirectoryEntry(ldapPath)){
IMailboxStore mbx = (IMailboxStore)de.NativeObject;
mbx.CreateMailbox(homeMDBUrl);
de.CommitChanges();
}


Works fine when run from a fat client. But when we call it from an IIS-based app, it errors with:

Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))

What gives?

This is a generic COM error saying "I couldn't reach a resource you need" (or something close to that). The best bet is that logging in to the Exchange server failed.

Exchange COM objects make requests under the identity associated with the process, not the thread. When trying to call into Exchange via COM (gotten from the NativeObject property on the AD object in .NET), the following do NOT work:
  • setting the user via impersonation in web.config
  • setting the user of all websites in IIS mmc
  • setting the user of the specific website in IIS mmc
  • setting the user of IIS Admin / W3SVC services
There is only one way to effectively get it to work: the ASP.NET worker process must itself run as the user who has rights to touch Exchange. This can be set in machine.config within the tag like this:

<processModel username="{domain\userid}" password="{password}" />


Setting that properly causes the ASP.NET worker process to run as that user at the process level. All other overrides are at the thread level and do not work.

With the machine.config file altered and IIS restarted (yes, you have to iisreset for changes to take effect), calling the code above within a web service / web application should work assuming your other parameters are correct.