Search
Close this search box.

Outlook Automation Without COM References

Friday, August 07, 2009 11:56 AM

I have recently completed work on a WPF application which required a reasonable amount of interaction with Microsoft Outlook. Specifically, we needed to implement two operations: firstly, we needed the ability to generate a new email and open it in Outlook (without sending it); and secondly, we needed to send an email via Outlook behind the scenes, without any user interaction.

There were a couple of additional requirements affecting how this functionality could be implemented. One of these was a technical issue, and the other was mandated by the business for whom the application is intended.

The first stipulation was that whatever code we used to communicate with Outlook needed to support multiple versions of Office. Our end-users run a mix of Office 2003 and 2007, and the requirement was that those currently running Office 2003 should be able to upgrade to Office 2007 without having to install a new version of the WPF client application.

The second restriction was that all emails should be sent using the standard company branding; in particular, all emails needed to use their standard email signature.

In order to solve the first problem, we wanted to avoid adding a reference to the Outlook COM interop assembly. Each version of Office has its own Outlook COM interface implementation (and therefore its own version of the Primary Interop Assembly, which stores the assembly metadata which allows .NET to marshal the calls to and from the COM interface), and backwards compatibility is by no means guaranteed.

Normally, when developing for a specific Outlook version, a reference is added to the COM interface via the COM Components tab of the ‘Add Reference’ dialog in Visual Studio like so:

This gives you strongly typed access to the properties and methods of the Outlook interface classes. The alternative to this is late binding, and in C#, this means but one thing: reflection, and lots of it (until C# 4.0, anyways).

Of course, the first problem you have when attempting to use reflection alone for Office automation is how to get an instance of the relevant Office application interface without directly instantiating one. One easy way to do this is using the application’s registry ProgID, and the Type.GetTypeFromProgID() method. Here’s an example of how to do this for Outlook:

1 : Type outlookAppType = Type.GetTypeFromProgID("Outlook.Application");
2 : object appInstance = Activator.CreateInstance(outlookAppType);

Once you have a new instance of the interface, from there on in, it’s just a case of calling Type.InvokeMember() against the relevant object instance to invoke the operations by name. To illustrate, here’s how you create a new mail message using the above application instance:

1 : object message = appInstance.GetType().InvokeMember(
        "CreateItem", (BindingFlags.InvokeMethod), 2: null, appInstance,
        new object[] { 0 });

The above code results in a new Microsoft.Office.Interop.Outlook.MailItem instance, which is the COM interface class used to represent a mail message.

There were plenty of examples around on the web of how to use the Outlook Interface to send or open a message, so once I had discovered how to get a reference to an interface instance indirectly, converting the rest of the code to perform the functionality using reflection was relatively trivial.

The next problem was slightly more complex to solve. The email body content produced by the WPF application is all formatted as HTML, however when we attempted to send or open the email, we found that the company-specific email signature footer content was not appearing as part of the generated email body.

The solution to this consisted of two parts: firstly, there is a rather obscure (and entirely unhelpfully named) property on the MailItemClass named GetInspector. For some reason, presumably known only to those who designed it, the act of calling this property and getting the resulting value (even if you don’t use the returned value for anything) seems to make the difference between the email signature being incorporated into the email body or not.

Yes, that’s right: GetInspector. It’s all so obvious now- I can’t believe I missed it. smile_sarcastic

However, there was one final hoop to jump though. The GetInspector() method loads the signature into the message body, however the signature is itself an HTML document. So now I had 2 HTML documents: the one produced by the application which needed to be sent, and the one containing the email signature which needed to be included.

The only sensible solution was to merge the two HTML documents. However you can’t simply append one to the other, otherwise you end up with a second opening HTML tag, after the first document’s closing HTML tag, which is not valid markup.

You need to ensure the HTML is well-formatted: the body contents of one document needs to be appended/prepended to the other to ensure we end up with valid HTML. I accomplished this by once again using the invaluable HTML Agility Pack’s HTML Parser. For anyone looking for a good .NET HTML Parser- this one comes highly recommended. Using this component, locating the body node of the HTML document produced by the WPF app, copying all of its child nodes to the HTML document produced by Outlook, then taking all the content of this combined document and using it as the email content was a breeze.

So now I had a class able to open/send messages via Outlook, without any COM references, which included the company-specific email signature- just the ticket! I also constructed the code to make it fairly generic, in order to leave open the option of adding support for other email clients at a future date.

As I mentioned earlier, each new version of Microsoft Office results in new COM interface versions for all of the Office applications, which may or may not be compatible with the old ones. So, whilst I am not claiming this solution is future proof, it does work with both Office 2003 and 2007, which was the intended aim.

The code, along with a test application, can be downloaded using the link at the top of the article.

This article is part of the GWB Archives. Original Author: Adam Pooler’s

Related Posts