If you have seen the error below when using objects from OOM in .NET 2.0 code then the information that Mason posted recently will explain a lot. The key to avoiding this error is to use the interfaces in OOM not the CoClasses. Mason explains why in his post...
Unable to cast COM object of type 'System.__ComObject' to class type 'Microsoft.Office.Interop.Outlook.MailItemClass'
...We have had a few customers looking for information on writing a custom foreign connector for Exchange 2007 and I finally got around to combining all my information into one document so that I could reuse it. I thought I would share it here as well to maybe save a few calls to our team...
What is a gateway connector?
“Gateway connectors generally use non-standard protocols, or proprietary APIs to connect Exchange to non-Exchange messaging systems” – [TechNet]Gateway Messaging Connectors Architecture
One of the most common examples of a gateway connector is an Exchange fax gateway that many ISVs have implemented for Exchange 2000/2003/2007. These gateway connectors allow a user to send faxes from Exchange clients by sending mail to a special address space like, 8001234567@MyFaxServer.com. Additionally when a fax is received the ISVs product could allow for delivery of the fax through the gateway connector to an Exchange client by converting the inbound fax to a mail message.
There are two key tasks that an Exchange gateway must perform…
Ø Receive outbound messages from Exchange
Ø Send inbound messages to Exchange
What is new in Exchange 2007?
Exchange 2007 does not support the same gateway model that Exchange 2000/2003 used. Rather than using MAPI and watching queues, the Exchange 2007 solution is file system based. To receive outbound messages from Exchange to be sent via the gateway service, you configure a foreign connector on the Hub Transport server which drops messages to a directory which the gateway service monitors. Conversely to send inbound messages from a non-Exchange to Exchange mailboxes the gateway services would create a file in a directory which Exchange monitors.
Receiving outbound messages from Exchange
The gateway service monitors a drop directory on an Exchange Hub Transport server to receive messages sent from Exchange clients that are to be sent via the non-Exchange gateway service. Address spaces which may be SMTP or non-SMTP are configured for each foreign connector to route messages to a particular drop directory.
Foreign connectors are created and configured using Exchange Powershell cmdlets.
“To use the interactive mode of the Exchange Management Shell to create a new Foreign connector
1. At the Exchange Management Shell command prompt, type New-ForeignConnector, and then press ENTER.
2. At the Address Spaces (0): prompt, type the address space to which this connector will send messages, for example, contoso.com, and then press ENTER.
Address Spaces (0):
3. Enter additional address spaces if this connector will send messages to more than one domain. After you enter all the address spaces, press ENTER to continue.
4. At the Name: prompt, type a name for this connector, and then press ENTER. The connector summary is displayed.” – [TechNet] How to Create a New Foreign Connector
Name:
Messages dropped in this directory will be in MIME format with TNEF (winmail.dat) attachments. The basic MIME data contains the header fields like TO and CC fields but you will not see the BCC, attachments, and body content. This information and other MAPI data are in the TNEF data which is in the winmail.dat attachment. The Exchange.Data.ContentTypes.TNEF namespace is used to iterate through the TNEF parts and extract the data from the winmail.dat that could be used by the gateway service for routing.
Send inbound messages to Exchange
The gateway service sends messages to Exchange by creating files in a replay directory. These are MIME formatted messages which are created using the new managed Exchange.Data namespaces.
The replay directory is monitored by Exchange. It checks for new messages every 5 seconds (this interval cannot be changed). Once a message is processed the file is renamed from *.eml to *.tmp. After the *.tmp file is converted to an SMTP message the *.tmp file is locked until the message is successfully queued for delivery. If the transport service is restarted then all the *.tmp files are renamed to *.eml and the processing starts over again. Messages dropped to the replay directory have some prerequisites which must be taken into account.
“A message file that is copied into the Replay directory must meet the following requirements for successful delivery:
· The message file must be a text file that complies with the basic SMTP message format. Multipurpose Internet Mail Extensions (MIME) message header fields and content are supported.
· The message file must have an .eml file name extension.
· X-Headers must occur before all regular header fields.
· A blank line must exist between the header fields and the message body.”
What is the difference between the Replay directory and the Pickup directory?
“Exchange Server 2003 uses a single Pickup directory for creating and submitting text message files. Exchange 2007 splits this functionality into separate Pickup and Replay directories. The Pickup directory is intended for users or applications to manually create new message files. The Replay directory is intended for the re-submission of exported Exchange e-mail messages and for receiving messages from non-SMTP connectors. This separation of duties allows for the appropriate security to be applied to one directory without affecting the functionality of the other directory.”
[TechNet] Managing the Replay Directory
Reading this kind of stuff almost makes we wish I was a consultant again...I say almost because then I think about requirement gathering, deadlines, budgets, debating the merits of agile development, etc...
The Exchange SDK team linked to this article on their blog the other day and I just had a chance to read through it. Exchange development seems like such a niche expertise sometimes and in the past the "tribal" knowledge it took to understand how to work with and extract data from Exchange could be intimidating for any developer. Yet there are so many possibilities to integrate the data in Exchange with all kinds of applications (CRM, IT management applications, scheduling, etc.) and Exchange 2007 Web Services start to change the game of Exchange integration because they are far easier to use and can be leveraged in so many different ways...
This article illustrates how to expose Exchange data through SQL so that any SQL developer can work with Exchange data just like they were working with a SQL view. The only Exchange development is using Exchange Web Services as a data access layer for populating SQL Server table-valued user defined functions...
"When you combine these technologies, you have a formidable new way to integrate, search, and analyze your Microsoft Exchange messaging data by using the full power of SQL Server, and to combine that messaging data with virtually any other data managed by, or available from, SQL Server."
...There are far more SQL developers out there so exposing Exchange data through SQL is a very empowering proposition. Especially the thought of expanding these samples to abstract dynamic views of Exchange folders and items through these SQL views could open up Exchange data to a whole new group of developers...
"...This sample is intended to educate and inspire you in your Microsoft Exchange development..."
...My wheels are turning, man this is cool stuff...So with Exchange 2007 Web Services the game is changed, instead of talking MAPI, COM, or .NET we are simply talking HTTP, XML, and in this case now we can talk SQL...Who can't integrate with that?
Here is some exciting news...The Exchange SDK team is blogging now at http://blogs.msdn.com/exchangedev/! This a great opportunity to get some direct access to the members of the product team who are responsible for API development and SDK documentation...
"...We (the Exchange SDK UE team) coordinate the Microsoft Exchange Development Blog and are made up of our Documentation Manager, Thom Randolph, two Programming Writers, Michael Mainer and, me, Ray Dixon, and our editor, Laura Graham. However, we aren't the only people who will post here. Contributers will also include Microsoft Exchange developers, testers, program managers, and more. In the near future, you will see content that describes techniques used in Microsoft Exchange application development, coding tips, and code samples, among other things..."
...They are looking for feedback too, please let them know what you want to see posted and provide feedback on how we are doing. These folks are very interested in what goes on in the "real world" of Exchange development..
"...please send us your ideas for blog posts. This helps us tailor the content to you. To share your blog post idea with us, just send an e-mail describing your idea to exsdkfb@microsoft.com..."
I just finished updating a support document which walks through creating a simple COM+ component and ASP.NET page to illustrate how you have to use CDOEXM with ASP.NET due to impersonation limitations of CDOEXM...enjoy...
...Use all code in this post at your own risk, etc...
CDO for Exchange Management (CDOEXM) is a management API for Exchange 2000 and 2003. While using CDOEXM in an ASP or ASPX web application you may get unexpected Access Denied or Catastrophic Failure errors. This is because CDOEXM only supports process-level impersonation and not the thread-level impersonation required by a web application using Windows Integrated for example. Due to impersonation limitations of CDOEXM, the only method supported by Microsoft for using CDOEXM from ASP or ASPX is to create a COM component and register that component with component services (COM+) running the component in a specific user identity.
Below is a walkthrough of how to create a simple .NET component which wraps CDOEXM and ADSI code to create users and associate mailboxes. There is also a function to illustrate the security context of the component which you can compare to the context of your application. We will illustrate this difference in a simple ASP.NET web application.
Environment Setup
This walkthrough is based off of Exchange 2003 and Visual Studio 2005 running off of Windows 2003. The code in this walkthrough is C#.
This code should be run from a Windows Server 2003 client that has Exchange 2003 Administrative Tools installed if you are targeting Exchange 2003 or an Exchange 2000 server. If you are targeting an Exchange 2000 server, you could also use a Windows 2000 client that has Exchange 2000 Administrative Tools installed. The CDOEXM version that is included in Exchange 2003 must be used when you access Exchange 2003. The Exchange 2003 CDOEXM can also be used to access Exchange 2000. The CDOEXM library that is included in Exchange 2000 is not supported for accessing Exchange 2003.
Creating the C# Component
Build and configure a COM+ component to host the CDOEXM code and ensure that it is running with the credentials of a service account and not inheriting any credentials from the caller.
Create the project…
Sign and Create Interop Libraries for ActiveDS and CDOEXM…
1. Right click on the CDOEXMComponent project in the Solution Explorer and click properties. In the signing tab check the box to “Sign the assembly” and create a new strong name key called “CDOEXMComponent.snk”.
2. Still in the project properties window, select the Application tab and click “Assembly Information…”, check the box that says, “Make assembly COM-Visible”.
3. Open the AssemblyInfo.cs and add “using System.EnterpriseServices;” to the top and the following lines at the bottom of the file…
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationName("CDOEXMComponent")]
[assembly: Description("Simple CDOEXM Component Sample")]
Add the CDOEXMSample class…
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.EnterpriseServices;
using System.Runtime.InteropServices;
using System.Text;
using ActiveDs;
using CDOEXM;
public class CDOEXMSample:
System.EnterpriseServices.ServicedComponent
{
public void CreateUserWithMailbox(string strFirstName,
string strLastName, string strSamName,
string strBaseContainer, string strHomeMDB)
//
// Start local variable definitions
DirectoryEntry oCont =
new DirectoryEntry("LDAP://" + strBaseContainer);
IADsContainer oDSCont = null;
IADsUser oUser = null;
IMailboxStore oMailBox = null;
try
oDSCont = oCont.NativeObject as IADsContainer;
oUser = oDSCont.Create("user",
string.Format("CN={0} {1}",
strFirstName, strLastName)) as IADsUser;
oUser.Put("sn", strLastName);
oUser.Put("givenname", strFirstName);
oUser.Put("samaccountname", strSamName);
oUser.SetInfo();
oUser.AccountDisabled = false;
oMailBox = oUser as IMailboxStore;
oMailBox.CreateMailbox(strHomeMDB);
}
finally
oCont.Dispose();
if (oDSCont != null)
Marshal.ReleaseComObject(oDSCont);
oDSCont = null;
if (oUser != null)
Marshal.ReleaseComObject(oUser);
oUser = null;
if (oMailBox != null)
Marshal.ReleaseComObject(oMailBox);
oMailBox = null;
public string GetIdentity()
AppDomain.CurrentDomain.SetPrincipalPolicy(
System.Security.Principal.PrincipalPolicy.WindowsPrincipal);
System.Security.Principal.WindowsPrincipal user =
System.Threading.Thread.CurrentPrincipal
as System.Security.Principal.WindowsPrincipal;
return user.Identity.Name;
Create and Configure the COM+ Component
1. Build the CDOEXMComponent project in Visual Studio
2. From the Visual Studio command prompt run “regsvcs CDOEXMComponent.dll”
3. Open “Component Services” from “Administrative Tools”
4. Under “Component Services”, “Computers”, “My Computer”, “COM+ Applications”, find “CDOEXMComponent” and right click on it then select “Properties”
5. On the security tab:
a. Under “Authorization” uncheck “Enforce access checks for this application”
b. Select “None” for “Authentication Level for Calls”
c. Select “Identify” for “Impersonation Level”
6. On the identity tab:
a. Configure “This user” as an account that has Exchange Admin privileges on your Exchange server. You could use the Administrator account for testing purposes.
7. Click OK
Create Web Application to Consume CDOEXMComponent
Create a folder and application files
1. Create a folder called “CDOEXMComponent” on the file system of your web server. For example, “C:\inetpub\wwwroot\CDOEXMComponent”.
2. Copy CDOEXMComponent.dll, Interop.ActiveDs.dll, and Interop.CDOEXM.dll from the output directory of the CDOEXMComponent project to a folder named “bin” under the folder you just created in Step 1.
3. Create a new file in the “CDOEXMComponent” folder called “web.config”
a. Copy the following XML and paste it in the web.config file
<?xml version="1.0"?>
<configuration>
<appSettings/>
<system.web>
<identity impersonate="true"/>
<compilation debug="true">
<assemblies>
<add assembly="System.DirectoryServices,
Version=2.0.0.0,
Culture=neutral,
PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<authentication mode="Windows"/>
</system.web>
</configuration>
4. Create another file in the “CDOEXMComponent” folder called “Default.aspx”
a. Copy the following code and paste it in the aspx file
b. Notice you will have to change the variables to match your test environment
<%@ Page Language="C#" %>
<%
// CreateUserWithMailbox Input, change to match
// your test environment.
string firstName = "Matt";
string lastName = "Stehle";
string samName = "mstehle";
string baseContainer = "CN=Users,DC=mstehle03,DC=extest,DC=microsoft,DC=com";
string homeMDB = "CN=Mailbox Store (MSTEHLEEX03),CN=First Storage Group,CN=InformationStore,CN=MSTEHLEEX03,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=mstehle03,DC=extest,DC=microsoft,DC=com";
// Print out the security context of the web application
Response.Write(string.Format(
"<br><b>Web Application Context:</b> {0}<br>",
System.Threading.Thread.CurrentPrincipal.Identity.Name));
// Create component
CDOEXMSample obj = new CDOEXMSample();
obj.CreateUserWithMailbox(firstName, lastName,
samName, baseContainer, homeMDB);
Response.Write("<br>Mailbox Created!<br>");
catch (Exception ex)
Response.Write("<br>Error when creating mailbox...<br>");
Response.Write(string.Format("Message: {0}<br>", ex.Message));
Response.Write(string.Format("Stack Trace: {0}<br>", ex.StackTrace));
// Print out the security context of the component
"<br><b>CDOEXMComponent Context:</b> {0}<br><br>",
obj.GetIdentity()));
%>
Create and Configure the virtual directory in IIS on your web server
1. Click Start, select “Run…” and type “inetmgr”, Click OK
2. Expand the “Web Sites” folder
3. Select a Web Site, right click and select New, “Virtual Directory…”
a. Click Next
b. For the alias, type “CDOEXMComponent”
c. Click Next
d. Set the appropriate path to the folder where you just created the ASPX file.
e. Click Next
f. Select Read and “Run scripts (such as ASP)”
g. Click Next
h. Click Finish
4. Right click the CDOEXMComponent virtual directory you just created and select Properties
a. Click the “Directory Security” tab
b. In “Authentication and access control” click the “Edit…” button
c. Uncheck “Enable anonymous access”
d. Check only “Integrated Windows Authentication” under “Authenticated access”
e. Click OK
f. Click OK
Talking a look at the results
When you access this page from another machine as different user than the administrator account configured in the COM+ component you will notice that the web application and the COM+ component are running in different security contexts. For example in my test, I was logged in a user called Green Giant and when I hit the web I got the following result…
Web Application Context: MSTEHLE03\ggiantMailbox Created!CDOEXMComponent Context: MSTEHLE03\Administrator
…The key here is that Green Giant is just a simple User in my test domain. He has no permissions to create new Active Directory Users or Exchange mailboxes. However, all he has to do is authenticate against the domain to gain access to this web form. The web form makes the call to our COM+ component which always executes the CDOEXM code in the context of Administrator, not matter who the calling process is running as. Again, this is because CDOEXM does not support thread level impersonation.
Therefore it is very important to pay attention to the directory security configuration in IIS and what users will have access to the web application in this scenario as well as who has rights to create and make calls to the COM+ component. Anybody who can call this component can create a mailbox.
...Sorry I haven't written much lately, I have plenty on the way. Counting down 3 months until my wedding and life just keeps getting busier and busier. Here is a look into the world of a support engineer at Microsoft and some related comments from Raymond Chen that I couldn't help but talk about since they are so on point...enjoy...
My job as support engineer is as much about communication as it is debugging. A large amount of that communication is done through email whether it be between me and customers, product team members, account managers, other support engineers, consultants, etc. A large amount of that traffic happens on internal discussion groups where people from all these different roles can post questions and provide answers to one another. This interaction is such a vital part of the support and consulting while also providing product teams with real life scenarios, bugs, and questions that they can use to improve their products. It exemplifies the cliche, "None of us is smarter than all of us."
Turns out there is an etiquette to be observed as well that has evolved over time. Most of it comes from respecting the time of others, especially the guys on the product team. This is a community interaction and although we couldn't function without it, providing answers to this groups is considered going "above and beyond". In the end it all works out, I may be able to answer some quesetions in the VSTO group or Outlook Programming group and in return I'm sure at some point someone will help me out on the Vista or Powershell groups. The key is to be respectful of the community and not abuse people's time and willingness to help out (especially people like Raymond Chen).
Raymond has a couple posts that talk about to the two most blatant violations of discussion group etiquette that are quite frequently broken at Microsoft, the dreaded "RESENDING:...." email due to "no response" and the ever popular "spam the world" tactic of trying to loop as many people into a problem as possible without letting them know. These posts hit home so precisely to life in these discussion groups that I couldn't help but link to them...
Email tip: People didn't answer your first email for a reason
Email tip: Don't ask the same question multiple times in different groups