Introduction

First of all, if you are thinking Outlook Express, just forget it. The only three ways of integrating with Outlook Express are (and this is just my guessing) by paying Microsoft a lot of money and by giving them a really good reason to why you want to do that, or by hooking in through the encryption-entry, or by creating a proper hook and hacking your way in there. I've done the hacking (due to missing funds) and it's not a pretty sight, it works but it's everything from clean.

So, this is not for Outlook Express, this is only for Outlook from Office.

Ever thought of extending Outlook to add that extra functionality that you so much wished for? Well, look no further. I will try to give you enough information so that you can do that. There are two ways to extend Outlook, one is pure ATL where the other is just about some COM-interfaces in a plain DLL. We could have discussion about what way to do it, and I'm sure there are people who prefer the ATL-version to the COM-version.

Referring to it as COM-version is wrong, well, not really, it is COM, but just for the sake of it, I'll refer to it as the Exchange-way. So why Exchange when we're talking about Outlook? It started with the old Exchange 4.0 client and Microsoft exposed a way to hook in to their mail client, and since Outlook is using the same model as Exchange, that entry point to the client has stayed and we should be grateful for that because its been tested through out all these years and it's 99.9% bug free where as the ATL-version isn't really that well tested.

I guess you've understood by now that this article isn't about the ATL-version,...

Also, I need to notify you that parts of this article are copied from MSDN, but with minor changes to allow people to actually understand what's written. Microsoft have never been really good at explaining their more complicated API's, don't know why but that's the way it is...

I've decided not to start out with code, but with some background information first, once that is covered, we will make a small addin that adds a button to a new toolbar. This addin will not cover the entire architecture, but it will not only give you an idea to what you can do with an addin, but more importantly, it will (hopefully) explain the basics so that you can continue on your own in less time than it took for me...

Background

You need to at least know what a COM-interface is, and no, I'm not talking about that IDispatch-derived thing that VC generates for you when you create an ATL-project, I'm talking about proper COM, the old one. You will also need to know a bit about MAPI, you don't have to be a guru, but you should at least know what it's all about. If you don't know what you just read, don't worry, you'll get enough information so that you know what you should search for.

Before I get going, when and if you want to debug your addin, select Outlook.exe to be the executable in Dev-studio.

Extending Outlook - the basics...

When you want to extend Outlook, what you do is basically create a DLL, export an entry-point, register the addin with Outlook by modifying the registry. But before I start talking about how that is done, let's go through the basics of how an addin is loaded, why and when it is released.

An addin is loaded several times, every time in a different context. Now, Outlook is nice enough to pass you enough information so that you can figure out in what context you've been loaded and by simply identifying the context, you can also figure out what you can do and what you shouldn't do.

In the table above, you can see three bars (five bars in total), each bar represents a different context. Each context has a timeline where the length of the bar is the time that context is loaded.

Before we look at this, its worth mention that Outlook keeps an array of addins, and that each addin has its own map of when it wants to be loaded.

So, if we take them in the load-order.

TASK:

As soon as you start Outlook, Outlook loads your addin in this context, This context is the one that last the longest, it will keep your addin loaded for as long as Outlook is running.

SESSION:

This context is created as soon as the user has logged on, that is selected a profile to use if multiple profiles exists, and that the user has established a session with MAPI (This could involve setting up a connection to Exchange server but that is not necessarily the case as you can configure Outlook to use Pop3 only, also referred to as Internet mail only)

This is a good place to figure out if you should continue to be loaded, an example could be that you installed your addin but you only want to be loaded in the profile X or if a certain condition is met, like if you can connect to some external resource such as a server)

VIEWER:

This context is the context that you will fight with most of the time because this context is created for each item you open in Outlook, but not only items, it can also be a new window you open in Outlook, basically every time you see a new window popup, there is a new VIEWER-context behind it. It's up to you to synchronize your resources here since your addin is not considered as a singleton.

So, how does Outlook know when to load your addin then? The best way to explain this is to look at how you register the addin in the first place.

Extending Outlook - registering the addin...

There are two ways of telling Outlook about your addin and that is either by using an ECF-file or through the registry. I won't describe the ECF-format because that is too long and not the purpose of this article, so open up your registry and browse to HKEY_LOCAL_MACHINE\Software\Microsoft\Exchange\Client\Extensions.

What you see here is, a list of addin's. Each entry in this list is an addin and apart from the name, the entry also tells Outlook when it wants to be loaded, what interfaces the addin implements and some quite uninteresting information (see MSDN: )

So, what can we see from this value? Let's break down the value to smaller pieces and I'll try to explain what they mean.

4.0

The version of Extension object implemented in the DLL (this is always 4.0).

C:\Program.......\scanemal.dll

The location of the DLL.

1

The ExchExtEntryPoint ordinal.

11000000000000

The list of contexts for which Outlook should install an Extension object.

1110000

The list of interfaces that the Extension object exposes.

The particular mail service that an Extension might use (not present in this example).

Now the first three items are not very hard to understand, so we'll look into the two items that are interesting instead.

First the context-map, the map that will tell Outlook when to load a given addin :

Context name

Map-position

Description

EE_CONTEXT_SESSION

1

Logged on to MAPI.

EE_CONTEXT_VIEWER

2

Looking at an object (Folder, perhaps).

EE_CONTEXT_REMOTEVIEWER

3

Using Remote Mail feature.

EE_CONTEXT_SEARCHVIEWER

4

Using Find feature.

EE_CONTEXT_ADDRBOOK

5

Address book is open.

EE_CONTEXT_SENDNOTEMESSAGE

6

Composing a Send message.

EE_CONTEXT_READNOTEMESSAGE

7

Reading a message.

EE_CONTEXT_SENDPOSTMESSAGE

8

Composing a Post message.

EE_CONTEXT_READPOSTMESSAGE

9

Reading a Post message.

EE_CONTEXT_READREPORTMESSAGE

10

Reading a delivery report, read report.

EE_CONTEXT_SENDRESENDMESSAGE

11

Re-sending a returned message.

EE_CONTEXT_PROPERTYSHEETS

12

Viewing the properties of an object.

EE_CONTEXT_ADVANCEDCRITERIA

13

Using Advanced Search.

EE_CONTEXT_TASK

14

The Client is running.

Next in line is the interface-map, this map tells Outlook what interfaces you implement. If you are unsure (don't know how you can be unsure to what interface you implement, but hey, who am I to judge...) you can always fill this map with 1's, Outlook will then query your base interface for all the interfaces it might need and it's up to you to give the right response to that. This map contains 9 interfaces that you decide if you want to implement (where you can map 7 of these 9) and there is 1 that is mandatory and therefore can't be put in this map.

If you want to add some buttons or menu-items, then this was the interface, today you would use a different interface, but it might be useful to implement this if you want to catch standard buttons. More on this later.

When you add an addin to the registry, Outlook caches the majority of the settings so that you need to tell him to reload the cache. There are three ways of doing this and I usually end up doing the first and the last, that way I won't have any surprises. Maybe not very clean but it works for me.

Add a magic value to the registry, Outlook is supposed to reload its cache when he finds this value.

The name of this value should be: Outlook Setup Extension.

And the value itself should be: 4.0;Outxxx.dll;7;00000000000000;0000000;OutXXX.

The only problem with this is that when you install the addin, you need to have full rights of the machine (or atleast read / write rights to HKEY_LOCAL_MACHINE)

Use an ECF-file. (See links for a description of the file format) By using this file, you don't have to edit the registry.

Delete the cache, (the file is named 'extend.dat'), that way you should be home safe. This is not recommended as this may have some side-effects, I have not seen any of these effects so far (and I've been developing addins for more than 5 years now) but there are supposed to be some.

As you can see, it's a callback that returns an instance of your addin-class. Now that doesn't really look like COM, does it? Well, it is and it is done the hard way. Outlook will actually query your object for different interfaces (see above) later on. What more can you see in this code? Well, to begin with, its a DLL that uses MFC. Now this is totally up to you if you want to use MFC or not, the addin itself doesn't require MFC, it's just me using it because I find it a lot easier to use and a lot less hard work than using AYL (MFC is Buggy, yes, I know, but this is just my personal opinion so please don't flame me for that...)

Extending Outlook - developing the core of your addin...

Start up your Developer Studio now and create a new project, make it a Regular DLL using MFC and call it OutlookAddin.

Not very hard, so now we have an entry-point to the addin. Now we need to add a class, a generic one called CMyAddin, let Dev-studio generate it for you, that way you will have it in a new pair of files.

Start by changing the class so that it derives from IExchExt, also, add the following functions so that your class looks like this :

It's worth mention that the functions AddRef, Release and QueryInterface comes from the standard IUnknown.

Now that you have the core of an addin, implementing these functions shouldn't be very hard, so let's do it: (I've skipped everything except the Install function, for the other functions, see the source-code for this article).

Ok, so now we have a basic extension that doesn't do anything except that it gets loaded into Outlook at startup and released when Outlook is shutdown.

Now, compile the project and sort any errors or warnings out before you go on to the next part of this article.

You will not find the includes if you are developing in Visual Studio .NET, you will need to find either the Platform SDK for MAPI or the includes from Visual Studio 6.

Extending Outlook - adding some buttons to the addin...

First, lets extend our class to add a toolbar and a button at startup. For that, we need to ask Outlook for an object called <name>. We will also need to import two DLL's that defines what the "Outlook Object Model"-objects (also referred to as OOM) look like.

// If you have Outlook 2000 installed on your machine, then use the
// following lines
#import"mso9.dll"#import"msoutl9.olb"// If you have Outlook 2002 installed on your machine, then use the
// following lines
#import"mso.dll"#import"msoutl.olb"// If you have Outlook 2003 installed on your machine, then use the
// following lines
#import"mso11.dll"#import"msoutl11.olb"

I have used the outlook 2000 version but it doesn't matter which version you import, (you don't have to have the version you are importing installed on your machine, it's enough to have these DLL's, but it's a lot easier to debug if you have Outlook installed...anyway, I've put the import line in my stdafx.h.

I have not included these files in the ZIP-file, you will have to locate them from your Outlook-installation folder. Apart from Copyright issues, they are big (Msox.dll is about 5.3 Mb).

One thing you have to remember is that if you are using the functionality that only exists in Outlook 2003, then obviously, that won't work in Outlook 2000, but you will still be able to run your addin as long as you don't execute that piece of code. Also, forget about this whole thing if you intend to run under Outlook 97 and Outlook 98. OOM existed back then but wasn't fully implemented. You will though be able to create a toolbar under Outlook 98 but it's not straight forward and there are no events for the buttons (among other things), so you will need to do some hacking (hook's and subclassing) if you want to detect when a button is clicked.

If you still want to be able to run this under Outlook 97 or 98, then look up the IExchExtCommands- interface and more specifically the function InstallCommands. (I will write more about this in another article, but only if demanded as this is not the most common thing to do these days as most users are running Outlook 2000 or later).

Before you can obtain the Outlook-object, you need to add another header-file called OutlookInterface.h in which you put the following:

Ok, so what do we have here? We have an extension, that at 'Install'-time obtains an Outlook-object.

Let's see what we can do with it now and how we can actually create a toolbar and add a button to it.

First step is to create some sort of container-class for the button(s) we are going to add, this class must be derived from IDispatch. This class will hook in to the IConnectionPoint of the button itself, and will that way be notified when the button is clicked. Now, this is just an example. So I won't put very much effort to make this look nice or make it very usable, I leave that to you. So, open up the file MyAddin.h and add the following between the line #include "OutlookInterface.h" and the class CMyAddin.

Now, to save some space in this article, I will not put the source for that class... it's kind of long, so if you have opened up the workspace for this article, then open the file OutlookButton.cpp and I will try explain the logic.

When we create an object of this type, we pass it a button as parameter. This button will be instantiated and we will hook up to the IConnectionPoint of that button. This is done in the constructor and in the function SetupConnection. When the button is clicked, the function Invoke is invoked (just couldn't resist writing that...) and then it's up to you to decide what to do. In this example, we will just display a message box to indicate that the button has been clicked.

In order to keep the code readable, we'll add a function called InstallInterface instead of putting the code in the Install function, and the prototype looks like this :

So, the only thing you need to do now is to register the DLL and to do this, in the sources for this article, you have a registry-file. You will need to edit that one and change the path so that it points to this DLL.

In the previous two parts, we've created a basic addin that added a toolbar, and when you clicked it, displayed a message box. So in this article, we will explore some more things you can do with an addin for Outlook. With this article, I will show you how you can log all incoming mails to a file. Not very impressive but it's only to show you some basic MAPI-operations.

If you haven't read the previous articles, then go back to them otherwise you wont understand very much of this.

Extending Outlook - Getting called for every mail that comes in...

First, let's extend our class to derive from IExchExtSessionEvents. This interface only has one method, and that is OnDelivery. This method is called for each and every mail that is delivered. There might be a slight delay from the moment they come in to that you are called, it all depends on rules and other addin's, but most of the time, you get called the split-second before the mail is actually displayed in the listview.

There is one thing that is extremely important and I can't stress enough about, and that is the return value of this function. If you look in MSDN, you can read the following:

S_OK

The extension object replaced Microsoft Exchange default behavior with its own behavior. Microsoft Exchange will consider the task handled.

S_FALSE

The extension object did nothing or added additional behavior. Microsoft Exchange will continue to call extension objects or complete the work itself.

Now, read the S_OK again, because if you do decide to return S_OK, consider the mail as lost unless you store the mail yourself. I'm not joking, the mail is lost if you do return S_OK.

I think you got the message by now. So let's get going on the code.

First, extend your class so that it looks like this (add the lines in bold)

Before we implement the OnDelivery, we need to change the method QueryInterface. This method will be called with a REFIID set to IID_IExchExtSessionEvents, so we need to add some lines to handle that case correct.

We have specified in the registry that we implement the IExchExt-interface, and also the IExchExtSessionEvents-interface so that when Outlook loads us, he will query us for the IExchExtSessionEvents-interface and when he does, we need to give him something back.

So, over to the OnDelivery. Add this function as follows and remember to return S_FALSE no matter if you handled the mail or not. It's not a return-code to indicate if your custom code worked or not. It indicates if you took care of the mail or not!

Now, what we want to do is to get a pointer to the mail, extract some information and log it to a file. So first we'll ask Outlook for the pointer by calling the method GetObject.

STDMETHODIMP CMyAddin::OnDelivery(IExchExtCallback *lpExchangeCallback)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
try
{
// The first thing we need todoisto obtain a pointer to the message
// that just arrived.
// When we do that, we also obtain a pointer to the message-store (see
// it as a database)
LPMESSAGE lpMessage = NULL;
LPMDB lpMdb = NULL;
if (SUCCEEDED(lpExchangeCallback->GetObject(&lpMdb,
(LPMAPIPROP*)&lpMessage)))
{

The method GetObject returns a pointer to the message-store and the mail that just arrived. The only thing we are interested in is the mail, so just ignore the message-store-pointer.

Before we continue, let's take a look at what a message is and how the data is stored in it. A MAPI-message is nothing like a mime-message. It's a binary blob and not a text-message. The data is stored in something called tags, propertytags. You could compare this to an ini-file where you have keys and values. In this case, the keys are tags.

These tags can either be predefined by MAPI, the most common ones like the subject and the body is a predefined tag, but you can also have something called named tags. Named tags are tags that you define in which you can store data that is private. This does not mean that the data is encrypted or hidden, anyone can extract this data.

1. Place OutputDebugString calls wherever appropriate (begin/end of all functions could be a good place to start).
2. Use _Crtxxx functions to produce a report file instead of debugger output (REPORT_MODE_FILE if I remember exactly).
3. Place a
#ifdef _DEBUG
DebugBreak();
#endif

in your DllMain (on the first line). This will invoke the debugger.
4. Register the dll to be called from Outlook and directly hit F5 from Visual Studio and choose Outlook as executable for debug.