Microsoft Outlook offers an API in the form of a COM object, which allows for automation of the application. The COM object can also be used from any .NET application. However, when you are trying to build a robust application that requires high availability, or if you want to use Outlook over and over again without restarting your application, you can run into some major issues. This article seeks to address those issues, providing a robust solution while working with Microsoft Outlook.
Let's say you have a solution where you want to add an Outlook PST store to the Outlook application dynamically, do some work, and then remove the PST. The problem comes in that Outlook does not release the PST until the Outlook process exits, and thus the PST remains locked by Outlook until that time, unable to be used in any other process. If your application is built in standard fashion, the Outlook process will not exit until your application also exits. This also means that any resources being tied up by Outlook will be tied up until your application exits. This is a problem if you plan on processing multiple PST's at a time, or if you want to use Outlook from an application such as a Service or in Remoting. Another concern is how Outlook may inadvertently affect your application- if Outlook were to crash, it could bring down your application as well. That is what we want to prevent.
Normally, to use outlook, you would just use the add reference dialog in VS.NET and set a reference to Microsoft Outlook Object Model. But here we are going to approach it differently.
First, we are going to prepare the assembly for use across several applications by installing it on the Global Assembly Cache (GAC). Keep in mind that when you deploy your application, you will have to use a setup project that will install the DLL on the GAC on the target machine. Installing an assembly on the GAC involves a few easy steps:
- From the Visual Studio .NET command prompt, set the path to the location of MSOUTL.OLB.
C:\Program Files\Microsoft Office\OFFICE11>
- Generate a strong name key pair using sn.exe. (This key pair will be used to create a strong name for the assembly, which is required for installing in the GAC.)
C:\Program Files\Microsoft Office\OFFICE11>sn -k keypair.snk
- Run the Type Library Import tool to create the .NET assembly from MSOUTL.OLB file. Notice the use of the /keyfile option to specify the keyfile created in step 2. This will give the assembly a strong name and sign it. I also created a new path for the output DLL to be located. This path can be any path of your choice. I chose the name NetOutlook.dll to indicate that it was a .NET assembly for outlook, but you can choose any name you prefer.
C:\Program Files\Microsoft Office\OFFICE11>tlbimp MSOUTLB.OLB
- Next, change directory to the path of the newly created NetOutlook.dll, and install the assembly on the GAC using gacutil.exe.
C:\NetOutlook>gacutil -i NetOutlook.dll
OK, now you should have a .NET assembly installed on the GAC that you can use from any .NET application. From here, I will discuss how to use this DLL from your application in a way that will address the issues identified in the beginning of this article.
The first step is to add a reference to the DLL for your application. Select Project->Add Reference, click the Browse button in the dialog box, then browse to the location of NetOutlook.dll.
Now your application has all the definitions of the types you will be using, but you will not be instantiating them directly. The trick to keeping the Outlook separated from your application is to load it into a separate
AppDomains are used to separate pieces of your application from one another. This way you can load and unload pieces of the application when they are needed and when they are finished. Also, a problem in one
AppDomain cannot corrupt the process of the other
Here is the code, written in a console application.
AppDomain domain = AppDomain.CreateDomain("Outlook");
In the following code, take note of the assembly name that is passed to
domain.Load(). The assembly name must be fully qualified. To determine the fully qualified name of your assembly follow the approaches in this reference.
System.Reflection.Assembly myassembly = domain.Load("NetOutlook,
NetOutlook.Application app =
NetOutlook.NameSpace ns = app.GetNamespace("MAPI");
NetOutlook.MAPIFolder folder =
Console.WriteLine( folder.Items.Count.ToString() );
Console.WriteLine("Press <Enter> to release Outlook");
Console.WriteLine("Press <Enter> to exit");