A COM AddIn for Outlook 2000

I was tired of purchasing antispam software which all failed one way or another, mainly by classifying mail from friends as spam. I was spending too much time fighting spam everyday even with these tools and I decided to do something about it. Since I use Outlook as my mail client software, I tought that it was a very good opportunity to familiarize with Outlook addins and design one that would get rid of that spam. This page presents my approach to the problem.

I had no choice, I had to develop a piece of software that would filter out spam from my in-box as if it was done by Outlook. In order to achieve this, I needed to find a way to integrate such a program into Outlook. Developing a  COM  Addin for Outlook and give it the capabilities of filtering out spam was the obvious solution.

Though I was familiar with COM, I was not familiar at all with the Office object model and in particular with the Outlook object model. I had to get some documentation before getting up to speed. Since I have Office 2000 installed on my computer, I had a look at Ref. 1. On the Web, I found several references showing how to develop an addin with Visual Basic (Ref. 2) and VC++ (Ref. 3) but the most useful reference has been Ref. 4 where the author uses the Delphi 6 Enterprise environment to connect to Outlook and build a user interface comprised of command bars, buttons and menu items. That was all I needed to proceed to the development of the addin.

The spam filtering part of the addin is essentially a two-step filtering process. It is used as follows from the Outlook user interface: 

  1. when the mail is downloaded from the mail server by Outlook, the name of each sender is automatically searched in a list of known spammers maintained by the application. If the name is in the list, the mail is deleted before it displayed in the inbox folder;
  2. the second step of the filtering process takes place once the user has reviewed the mail items left in the inbox. Clicking on a button filters out all the mails whose sender is not in the list of friends maintained by the application and append all the filtered-out senders to the list of spammers.

Overview of Office/Outlook addins

An Office addin is a COM Automation component that dynamically extends/enhances and controls any Office suite of applications. There are two types of addins: 

Here, we use this style of addin. Office 2000 and later support a new, uniform design architecture for building such application add-ins. Typically such addins are housed in ActiveX dlls (inproc server) and can be dynamically loaded and unloaded by the user through the main application.

The IDTExtensibility2 interface

In this new architecture, the communication between the Offica application and the addins are made through the IDTExtensibility2 interface, an interface  that all Office addins must implement. It is a set of five events that provides functionality for a COM add-in. These events are provided as an interface only - no code is provided for them, only procedure stubs. All five events must be present in the compiled code, or compilation will fail - any events that you don't use in your add-in should have at least a comment line in their procedures, to prevent the compiler from removing them.

In the Delphi 6.0 programming environment, this interface is declared as follows in the AddInDesignerObjects_TLB.pas file:

  //IDTExtensibility2 methods
  procedure OnConnection (const Application: IDispatch;
    ConnectMode: ext_ConnectMode; const AddInInst: IDispatch;
    var Custom: PSafeArray); safecall;
  procedure OnDisconnection(RemoveMode: ext_DisconnectMode;
    var Custom: PSafeArray); safecall;
  procedure OnAddInsUpdate(var Custom: PSafeArray); safecall;
  procedure OnStartupComplete(var Custom: PSafeArray); safecall;
  procedure OnBeginShutdown(var Custom: PSafeArray); safecall;

and these event handling functions act as shown in the table below:

Event When It Occurs
OnAddInsUpdate When any add-in is connected or disconnected in the COMAddins collection.
OnBeginShutdown When Outlook begins its shutdown routines, if the add-in is connected.
OnConnection When the add-in is connected to Outlook.
OnDisconnection When the add-in is disconnected from Outlook.
OnStartupComplete When Outlook completes its startup routines. Only if the add-in connects at startup.

Registration of the addin

Implementing the IDTExtensibility2 interface is a necessary condition for a module to be a COM addin but it is not sufficient. The COM object must be registered with Windows and the Office application it is an addin to: in this case, Outlook.

Registration with Windows

 As it is the case for all COM objects, the COM addin must be registered with Windows. Such registration implies that the GUIDs of the addin and its type library will be added to the list of CLSID in the registry, and that the GUIDs of the interfaces will be added to the list of interfaces in the registry.

Such registration is performed by choosing the Run|Register ActiveX server in the Delphi IDE if the COM object is an in-process server or by launching it it if the COM object is an out-of-process server.

Registration with Outlook

In order for  the Office application to know which modules to call when it is launched, there is a supplementary requirement for the COM addin: it must notify the Office application that it is listening. To register the COM addin with the host application, we also need to create a couple of registry subkeys under the hive


where ProgID refers to the addin COM object's unique Programmatic Identifier(ProgID). The other entries through which the addin can provide information about itself and specify loading options to the host app are:

The Outlook Object Model

In the Outlook Object Model, the Application object is at the top of the object hierarchy and it represents the entire application. All other objects for that application are below the Application object. The second tier consists of a high-level categorization of objects. The remaining tiers include a variety of additional objects that are used to access the functionality that the second-tier objects contain. We will come back with more on the Outlook object model when appropriate. For more details, the reader is referred to MSDN of the appropriate Office and Outlook help files.

Using the IDTExtensibility2 interface is a necessary but not a sufficient condition to communicate with the Office and Outlook object model. There needs to be Delphi programs that ease using the interfaces and prevent the programmer from having to deal with the details of the interfaces: such programs are often called wrappers because they conceptually wrap the functionalities of the objects of the model.

Some of these Office wrappers are already loaded with Delphi in the ..\Delphi6\Ocx\Servers directory: Office2000.pas, Outlook2000.pas, and others whereas others must be imported from the menu Projects|Import Type Library|... (Microsoft Office 9.0 Object Library..., Microsoft Outlook 9.0 Object Library and others). Since none of those surfaced the TCommandBars and the TCommandBarButtons objects, I had to produce the unit OfficeWrappers.pas unit that does just that.

Development of the SpamFilter COM addin

Now that we have laid the foundation of our work, we are ready to proceed with the development of our Spam Filter addin.

Initiating the project

As the objective of the project is to generate an in-process-server that will reside in a dynamic library (.dll) and, as a library hosting a COM object is defined in Delphi as an ActiveX library, I started the project by selecting File|New|Other... and then moving to the ActiveX page, I selected the ActiveX Library option. This generated a project file that I have saved as SpamFilter.dpr.

View of the Automation Object Wizard
As I wanted the project to be an automation object, I selected File|New|Other..., then moved to the ActiveX page and, this time, I selected "Automation Object" option. In the resulting automation object wizard, I entered the name of the coclass as Spam and left the Instancing and the Threading Model to their default values of Multiple Instance and Apartment, and clicked on OK.

This opened the type library editor. There is nothing more to do with it at this time as the definition of the coclass in the wizard has generated two entries in the type library editor: an interface named ISpam and a coclass named Spam. This automatically generated the Pascal version of the interface that I saved as SpamFilterMain.pas as well as the type library file called SpamFilter_TLB.pas.

In the SpamFilterMain.pas file, one finds a class called TSpam based on TAutoObject and implementing the ISpam interface. The TAutoObject class is used as a base class for VCL ActiveX control classes and provides support for ActiveX Automation objects. The ISpam interface has no method and no property and, therefore, no implementation is needed.

As we want the server to be an addin for Outlook, it needs to be notified of Outlook events (including launch and termination) through the implement the _IDExtensibility2 interface located in the AddInDesignerObjects_TLB.pas.

Starting the automation server

When Outlook is launched, it communicates with all the registered processes that implement the IDTExtensibility2 interfaces on the system through their OnConnection method and transmits them a reference to its IDispatch interface through the Application argument. This reference is immediately stored through the statement:

FApplication:= Application as _Application;

which downcast the IDispatch reference to _Application. This statements is followed by

 FOutlookApplication:= TOutlookApplication.Create(nil);
 FOutlookApplication.OnOptionsPagesAdd:= OnOptionsPagesAdd;

statements whereby a local wrapper of the client (Outlook) application is created and connected to the _Application interface declared in Outlook2000.pas and sent by Outlook. The rest of the OnConnection method is used to initialize the add-in. This interface will be used later for the installation of the command bar and menu items on the Outlook's user interface.

Building the user interface

As I said previously, I like to use the IDTExtensibility2 interface function OnStartupComplete to add the user interface to Outlook and this is exactly what I am doing here. In this section, I will add a command bar to the Outlook command bars collection, I will put three buttons on it and I will add two menu item to the Outlook "Tools" menu.

Inserting a command bar

In Office applications, menus and toolbars are combined into a fully programmable collection called CommandBars. CommandBars are common sharable programmable objects that are exposed by all Office applications as a part of it's object model . CommandBars represent a unified mechanism through which individual toolbars and menu items can be added to the corresponding application. Each CommandBars collection is comprised of individual CommandBar objects. .Each CommandBar object again contains a collection of CommandBarControl objects, called CommandBarControls.

CommandBarControls represent a complex hierarchy of objects and sub-objects that comprise it's object model. A CommandBarControl can itself contain a CommandBar object, accessible through the CommandBar property of the control. Finally, each CommandBarControl object, within the CommandBarControls collection of controls, can be either a CommandBarComboBox (toolbar combobox), a CommandBarButton (toolbar button), or a CommandBarPopup (popup menu).

The task of populating the Outlook command bar collection with a new command bar named "SpamFilter Command Bar" is done with the following code snippet:

  CmdBars:= CommandBars(FApplication.ActiveExplorer.CommandBars);
  // Check if command bar "Spam Filter Command Bar" already exist
  for i:= 1 to CmdBars.Count do
    if CmdBars.Item[i].Name = rsCommandBar then FCommandBar := CmdBars.Item[i];
  // if not, add it
  if FCommandBar = nil then // EmptyParam declared in Variants.pas
    FCommandBar := CmdBars.Add(rsCommandBar, msoBarTop, EmptyParam, EmptyParam);
  FCommandBar.Set_Visible(True); //Command bar is added and visible

where CmdBars is declared locally as a CommandBars type, a collection of CommandBar objects and rsCommandBar is a resource string containing the name of the command bar. 

In the first statement, the current Outlook command bar collection is retrieved and then scanned to detect the "Spam Filter Command Bar". If the process succeeds, the existing command bar is used. If it fails, a new command bar is created. The private variable FCommandBar then contains a reference to the "Spam Filter Command Bar".

Inserting two buttons in the command bar

The insertion of the two required buttons on the command bar is processed as follows (only the code snippet for the generation of the first button is provided):

  // create the first button
  FButtonFilter:= TOutlookCommandBarButton.Create(nil);
  with FButtonFilter do
    ConnectTo(Control as _CommandBarButton); // associate it with the control
    Caption:= rsFilterSpam; // define its label
    Style:= msoButtonIconAndCaption; // define its style
    PasteFace; // paste the icon on the button
    Visible:= True; // make it visible
    OnClick:= OnButtonFilterClick; // assign OnClick event handler

The TOutlookCommandBarButton class is a wrapper for the Office CommandBarButton object located in the OfficeWrapper.pas file.

Inserting two menu items in the "Tools" menu

Similarly to add a new menu item to Outlook's Tools menu, we do the following. The ActiveMenuBar property of the CommandBars collection returns a CommandBar object that represents the active menubar in the container application(i.e. Outlook for us).Next we query for the active menubar's controls collection (CommandBarControls) through GetControls method. Since we want to add a popup menuitem to Outlook's Tools (6th position) menu,we retrieve the 6th item in the activemenubars control collection and straightaway call Add method to create a new menuitem and attach it to Tools menu. There's really nothing new here

The following code snippet shows how to add a menu item to the Outlook "Tools" menu:

  // now, lets create a new menu item in the "Tools" Menu
  ToolsBar := CommandBars(FApplication.ActiveExplorer.CommandBars)['Tools'];
  MenuIntf := ToolsBar.FindControl
    (EmptyParam, EmptyParam, 'SpamFilter', EmptyParam, EmptyParam);
  if (not Assigned(MenuIntf)) then // if assigned, use it, if not create it
    MenuIntf := ToolsBar.Controls.Add(msoControlButton, EmptyParam,
      EmptyParam, EmptyParam, True);
  FManageFriendlyMenu:= TOutlookCommandBarButton.Create(nil); // create menu item
  with FManageFriendlyMenu do
    ConnectTo(MenuIntf as _CommandBarButton); // associate it with above control
    Caption := rsManage; // give it a label
    Visible := true; // make it visible
    OnClick := OnManageMenuClick; // associate OnClick event handler
  end; // with FManageMenu do

where ToolsBar is declared locally as a CommandBar type and rsManage is a resource string with the value "Manage Spam Filter''s list of friends". The addition of the second button is performed with similar code (the first line is not repeated as the variable ToolsBar is already defined).

Adding items to right-click menus

Inserting menu items in the context menus displayed by Outlook when right-clicking on Outlook Explorers and Inspectors has been by far the trickiest undertaking of this project. I found out that it was a hack... 

The Outlook user interface does not provide any means to customize the context menus, although you can customize other menus and toolbars. The Outlook object model contains a CommandBars object that allows manipulating menus and toolbars programmatically, but the context menus are not exposed as a menu like other menus and toolbars are. In fact, Outlook generates the context menus dynamically and immediately before they are displayed for the first time. In Outlook 2000, this command bar is called "Context Menu" whereas it is called "Folder Context Menu" in Outlook 2002.

Exposing the CommandBars' OnUpdate() event

As this object is only available after right-clicking in an Explorer window, we must find an event that will notify us of such event: this event is the OnUpdate event of the CommandBar collection that fires whenever a command bar is changed. The event is triggered by any change to a command, or by the state of a bar or command bar control. These changes can result from pressing a button, by changing text, or by selecting a cell. It fires too when new mail is received!

I have developed the CommandBars wrapper class TCommandBarsWrapper in the OfficeWrappers.pas unit. It refers to the

  IID__CommandBars: TGUID = '';
  DIID__CommandBarsEvents: TGUID = '';
  CLASS_CommandBars: TGUID = '';

declared in Office_TLB.pas. The event is exposed as follows in the OnStartupComplete() method:

  FCommandBars:= CommandBars(FApplication.ActiveExplorer.CommandBars);
  FCommandBarsWrapper:= TCommandBars.Create(nil);
  with FCommandBarsWrapper do
   AutoConnect:= False;
   OnUpdate:= UpdateBars;

 where the UpdateBars() method is the event handler for the event.

Populating the right-click menu

This event is fired whenever a CommandBar has to update, and so we'll have to find the right click menu and add items to it. The right click menu has a fixed name "Context Menu". Here, we'll have to enumerate CommandBars to find it. Our task is as follows:

When these steps were executed in the early versions of the program, the statement adding the control crashed Outlook every time. I found that the context menu  CommandBar objects is locked by Outlook; in order to call the Add method, I had to remove the protection of the CommandBarControl object, or the call to Add method would fail. I used the Protection property of CommandBar to remove the protection.

Without the reset, the context menu shows up customized (with added controls and buttons) only once at the first right-click. After that, right-clicking generate the standard menu without the extra controls and buttons. With the reset in, the context menu shows up customized all the time given that the first right-click occurs on a spot that produces the context menu that has the most menu items. Let me explain.

When one right-click on the list of e-mails on the Explorer, the standard context menu contains 13 menu items. When right-clicking on a blank part of the explorer, the standard context menu contains 8 menu items. The code works perfectly if the first right-click occurs on the list (producing 13 items + added ones) and all the right-clicks that follow produce the customized menu. But if the first right-click occurs on the blank part, the shorter context menu is produced correctly but any future attemps to right-click on the list (producing a longer menu) causes an "unknown software exception" that kills Outlook. The code works fine if the user clicks on the list of e-mails - a first right-click on area producing a shorter menu will cause the exception and kill Outlook when an area producing a longer menu.

Is it likely that Outlook allots memory for the context menu resulting from the first right-click and cannot allocate extra memory if more space is required (longer menu) later.  Is it a bug with Outlook 2000? Is there another way to produce the customized menu? I cannot include the feature in the software if it is that unsafe.

I raised this question on the microsoft.public.developer.outlook.addins newsgroup on July 6th, 2006. The asnwer was nearly immediate.

Reply by Ken Slovak on July 6, 2006 - 09:04

Doing what you're doing is non-supported and is a hack. It usually works but there can be problems and nothing is guaranteed. Outlook 2007 adds full support for all the different context menus but for Outlook 2000 - 2003 you have to do what you're doing to customize a context menu.

Have you looked at the code samples and discussions about customizing the context menus at That has probably the most information about this.

Pasting icons on Outlook buttons and menu items

As one will easily notice, pasting an icon on a button through COM is not as simple as putting one on a speed button in Delphi. This is the role of the procedure IconToClipboard that appears as the first statement of the procedure OnStartupComplete

that opens the way for the pasting of the "gtro" icon on the Spam Killer buttons. In preparation for this operation, I have created a resource file called resource.res that I have associated with the addin by adding the statement {$R resource.res} in the project file. In this resource file, I have put a bitmap identified by the number 101.

The code of this procedure follows:

procedure TSpam.IconToClipbard;
  Image: TImage;
  rh: THandle;
  rh:= LoadBitmap(sysinit.HInstance, MakeIntResource(101)); // load the icon
  Image:= TImage.Create(nil); // create Image
    Image.Picture.Bitmap.Handle:= rh; // pass the handle
    Clipboard.Assign(Image.Picture.Graphic) ; // Assign to the clipboard
  end; // icon has been copied to the clipboard

In the first statement of the procedure, the variable rh receives the handle of the bitmap that is identified by 101 in the resource file. This handle is then passed to a newly created TImage component et then copied to the clipboard, ready to be pasted on the button. As the content of the clipboard will not be modified during OnStartUpComplete, the paste operation is performed for each button by the statement


The function fails if the clipboard is empty.

Trapping Outlook ItemAdd event

As the program has to filter out >known spammers when e-mails are downloaded from the mail server, the ItemAdd event of the inbox folder of Outlook has to be trapped and handled

The event is trapped with the following statements at the end of the OnStartupComplete method:

  FNameSpace:= FOutlookApplication.GetNamespace('MAPI'); // get Outlook folders collection
  FInBoxFolder:= FNameSpace.GetDefaultFolder(olFolderInbox); // select the InBox folder
  FMyInBoxItems:= FInBoxFolder.Items; // get the InBox folder's items collection
  FMyItems:= TItems.Create(nil); // class TItems is declared in Outlook2000.pas
  with FMyItems do
   AutoConnect:= False;
   ConnectTo(FMyInBoxItems); // connects TItems object to Outlook
   OnItemAdd:= ItemAdd; // event handler method
  end; // with ...

where the event handler ItemAdd must have the format defined for it as a TItemsItemAdd type in Outlook2000.pas

procedure ItemAdd(Sender: TObject; var Item: OleVariant);

Event handling routines

Putting together a couple of toolbars and menu items is not very useful unless we can write command handlers that respond to their events. Now that we have generated the user interface on Outlook and have initialized our add-in to trap one Outlook event, we need to implement the filtering of the spammers out of incoming e-mails and provide the actions that the user wants to execute by clicking on them: filter out those e-mails whose sender is not in the list of friends, add and remove friends from the list.

The list of friends is stored in a text file called "Friendly.txt" whereas the list of spammer is located in "Spammer.txt". These files are loaded each time the program is executed and saved when any of them has been changed by the program.

Filtering out spam

This is the program's main task. When the "Filter spam" button is clicked, all the e-mails contained in the Outlook inbox are scanned as follows in the OnButtonFilterClick() method:

  1. Get to the Outlook folder collection and select the InBox folder;
  2. For each item in the list of mail items in that folder, check if the name of the sender is in the list of friends
    • if the name of the sender is in the list of friends, keep it;
    • if it is not, delete it and insert it in the list of spammers;
  3. Save the file containing the list spammers if it has been modified.

Registering friends

Registering friends is the first level of management of the SpamFilter program. In fact, clicking on the "Register friends" button will initiate the registration of the sender of the selected e-mail as a "friend". This is done as follows:

  1. Go to the active explorer and count the number of items that it contains; If there is none, quit;
  2. Is there an item or items selected in the active explorer. If not, quit.
  3. For each item in the selection,
    • register the name of the sender in the list of friends; and
    • remove it from the list of spammers if it was in this list;
  4. Save the files that have been modified in the process.

Managing the list of friends

The second level of management of the program has to with removing senders from the list of friends or from the list of spammers. This is done through the menu items "Manage SpamFilter's list of friends"and "Manage SpamFilter's list of Spammers" in the "Tools" menu. Both event handlers generate a form showing the content of each file as shown in the following code snippet:

procedure TSpam.OnManageMenuClick(Sender : TOutlookCommandBarButton;
  const Ctrl: CommandBarButton; var CancelDefault: WordBool);
  fFormManager:= TFormManager.Create(nil);
    fFormManager.FriendlyFileName:= FriendlyFileName;
  end; // try...finally

This code presents the user with a modal form (an ordered TListBox) from which a selection can be made. Clicking the "Delete selected items" button permanently remove the selected friends from the list of friends.


The addin is registered with Windows as detailed in the overview above and the registration with the host application (Outlook) is done automatically with the registration with Windows by sub-classing the TAutoObjectFactory class and overriding the UpdateRegistry() method, a method that is called to specifically register or unregister the server. This is done as follows:

procedure TSpamFilterFactory.UpdateRegistry(Register: Boolean);
 Reg: TRegistry;
  Reg:= TRegistry.Create;
    if Register then
      if Reg.OpenKey('Software\Microsoft\Office\Outlook\Addins\' + GetProgID, TRUE) then
        Reg.WriteString('FriendlyName', 'SpamFilter for Outlook');
        Reg.WriteInteger('LoadBehavior', 3);
      if Reg.KeyExists('Software\Microsoft\Office\Outlook\Addins\' + GetProgID) then
        Reg.DeleteKey('Software\Microsoft\Office\Outlook\Addins\' + GetProgID);

  end; // try...finally

Storage for the lists

The list of friends (Friendly.txt) and the list of spammers (Spammers.txt) maintained by the spam filter are stored in the folder that Outlook uses for storing application-specific data files. This is normally "C:\Documents and Settings\<current user>\Application Data\Microsoft\Outlook\" and this folder is reached using the following code in the OnConnection() implementation of the IDTExtensibility2 interface:

  FSpecialPath:= GetSpecialFolderPath;
  FriendlyFileName:= FSpecialPath + '/Microsoft/Outlook/Friendly.txt';
  SpammerFileName:= FSpecialPath + '/Microsoft/Outlook/Spammers.txt';


function TSpam.GetSpecialFolderPath: string;
  Buf: PCHar;
  Buf:= StrAlloc(MAX_PATH);
    if SHGetSpecialFolderPath(0, Buf, CSIDL_APPDATA, True) then
      Result:= Buf;
      Result:= '';
  end; // try...finally

and SHGetSpecialFolderPath(), declared in the ShlObj.pas unit, provides a straightforward access to the special folders of the Windows shell. Here, we access the special folder referenced by the CSIDL "CSIDL_APPDATA", the folder where applications store application-specific data.


In this article, I have described the mechanism through which an add-in is incorporated into Outlook, how it traps and reacts to incoming e-mails, how the user interface is displayed and how it is implemented to meet the requirements of a spam filter.

My preference for the user interface would have been one button for the filtering of the spam and  menu items in the context menu displayed by Outlook in each explorer to host the registration of friends and the registration of spammers. This is an undocumented feature, a hack and my approach to solve this problem is exposed above in the section entitled Adding items to right-click menus.

Finally, the two-phase filtering algorithm that I have used is not as effective as I believed it would be. Since I implemented the filtering of known spammers and put in the list of known spammers all the mails that were filtered out in the second phase of filtering, the list of known spammers has grown to about 4,000 names of spammers and less than 25% of the spam e-mails is trappedt in the first phase of the filter operation. Maybe these guys are inventing new names every morning...

A few months ago, I had to purchase two new computers equipped with Window Vista, and I don't use this program anymore since Outlook 2000 is no longer supported under this operating system: this program has not been either used or tested under Windows Vista.

The source code can be downloaded from this site.


  1. Files "VBAOUTL9.CHM"  and "VBAOFF9.CHM"
  2. Microsoft Outlook Programming
  3. Building an Office2K COM addin with VC++/ATL
  4. BabelFish for Outlook by Dmitry Streblechenko, a COM add-in for Outlook 2000/2002 that uses BabelFish Simple Object Access Protocol: a simple XML based protocol to let applications exchange information over HTTP") ?> >SOAP service from to translate any Outlook message to a dozen different languages.

These references have been extremely useful during this study, in particular, Ref. 4 that has inspired the construction of the user interface. Indebtness is hereby acknowledged.

This code was developed for the pleasure of it. Anyone who decides to use it does so at its own risk and agrees not to hold the author responsible for its failure.

Questions or comments?
Last modified: September 3rd 2014 12:33:16. []