Introduction

Recently, I wrote an Outlook2000 COM addin as a part of a project to build a CRM tool. While coding the project, I thought this would make a good topic for an article especially since most of the Office related stuff I found on the Internet were VB/VBA related and almost none with ATL.

The code in this article is not optimized and the general approach has been kept simple for the reader to follow. Since I took quite sometime to write this, despite my best efforts, in case of any errors or omissions, kindly drop me a mail. If you like this article or found it interesting read, I'd be glad if you could give me a good rating and mail me your comments.:) Thanks.

Overview

Through this article/tutorial, we will learn how to program an Outlook2000/2K+ COM addin using a pure ATL COM object. We'll startout by writing a basic functional COM addin. Then I'll show you how to add standard UI elements like toolbars and menu items to Outlook and how to respond to their events. Next we'll add our own propertysheet for the addin to Outlook's Tools->Options. Along the way we'll see the relevant registry keys and also take a look at useful features of the ATL Wizards and learn to use them effectively

Although we'll be writing an Outlook2000 COM addin, COM addins for other Office2000 applications like Word,Access etc can be built very similarly. Except a couple of minor things like registry keys, for instance, the fundamentals remain the same.

I'm assuming that you are a VC++ COM programmer, and have had some experience with ATL based component development and OLE/Automation, although this is not strictly necessary. To build and test the addin, you must have MS Office 2000 installed on your system, or at least Outlook2K.The project code has been built with VC++ 6.0 sp3+/ATL3.0 and tested on Win2K with Office 2000 installed.

Getting started

An Office addin is a COM Automation component that dynamically extends/enhances and controls any Office suite of applications. Microsoft 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.

An Office COM addin must implement the IDTExtensibility2 interface. The IDTExtensibility2 dispinterface is defined in the MSADDin Designer typelibrary (MSADDNDR.dll/MSADDNDR.tlb) file usually present at the location <drive>/Program Files/Common Files/Designer.

All COM add-ins inherit from IDTExtensibility2 interface and must implement each of its five methods.

OnConnection and OnDisconnection, as their names suggest, are called when the addin loads and unloads from memory. The addin can be loaded either during application startup, by the user or through automation and the enumerator ext_Connect denotes these connection modes.OnAddinsUpdate is called when a set of COM addins changes.OnStartupComplete is called only if the addin was loaded during application startup and OnBeginShutdown is called if the addin was disconnected on host application shutdown.

Registering an addin

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:

FriendlyName - a string value - this is the addin's name as displayed by the host app.Description - a string value - a description of the addin.LoadBehavior - a DWORD value. - A combination of values that determine how the addin will be loaded by the host app.Set to 0x03 to load on app startup or 0x08 for user controlled activation.CommandLineSafe - a DWORD value. Set to 0x01(TRUE) or 0x00(FALSE).

For a full description of the all values and options, please consult MSDN documentation.

Building a minimal COM addin

OK, now we know enough to go ahead and code a minimal Outlook2K COM addin.To build the addin project files fireup VC++ IDE, create a new ATL COM Appwizard project and name it OutlookAddin. Remember,if you name it anything else it probably wouldn't work.(just kidding!)

Next goto Insert->New ATL Object menu and insert a new ATL simple object to the project by choosing Objects from Category and Simple Object from Objects list in the ATL Object Wizard dialog. Click Next and type Addin as ShortName.In the Attributes tab check Support ISupportErrorInfo. Accept the default options for the rest and click OK.

So far the Wizard has given us an Automation-compatible,dispinterface-savvy inproc COM object housed in a dll. By default,a registry script(.rgs) to add the COM object specific registry entries have also been handed to us. Build the project and checkout if everything is in order.

If you are the eager-beaver type like me, you still need to compile your project's .idl file at the very least before moving on . So do that now.

Next we will write our addin specific code to implement IDTExtensibility2.This is where we let the Implement Interface ATL Wizard kick and make our life a lot easier. In ClassView right-click on CAddin class and choose Implement Interface. This brings up the ATL Implement Interface Wizard.Next click on Add Typelib and in the Browse Typelibraries dialog, scroll down and check Microsoft Add-in Designer(1.0) and click OK. Next check _IDTExtensibility2 interface from the list under AddinDesignerObjects tab of Implement Interface dialog and click OK.

The wizard implements the selected interface for us by adding a default implementation for each of the 5 methods of _IDTExtensibility2 to the CAddin class and updating the COM_INTERFACE_MAP(). Of course each of the methods just returns E_NOTIMPL and it is upto us to add code to do something useful. But for now, our functional COM addin is ready, except the for the necessary registry entries which we'll add next.

To register our addin with the host application,in this case Outlook2000, open the project's Addin.rgs registry script file (under FileView->Resource Files) and add the following to the end of the file.

Since we want our addin to load at a startup, LoadBehavior is set to 3.Now build the project. If everything is in order, the project gets built successfully and registers the addin. To test the addin, either run the project and specify the fullpath to Outlook2K(\Program Files\Microsoft Office\Office\Outlook.exe) in Executable for Debug Session, or run Outlook2K outside the VC++ IDE after you have registered the dll. To check if our addin has been registered successfully, in Outlook, goto Tools->Options and under Other tab click Advanced Options->COM Addins. An entry for our COM addin should have been added to the Addins Available list; the string is what we specified as 'FriendlyName' in our registry script.

While an addin can be programmed for different uses, there are certain common tasks. Typically, this includes adding UI elements like toolbars/toolbands and menu items to Outlook, through which the user can control the addin. By clicking on these buttons and menu items, the user can access the addin's functionality. So next up we'll tackle such toolbar and menu item additions.

Command and Conquer

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 comprises of individual CommandBar objects. .Each CommandBar object again contains a collection of CommandBarControl objects, called CommandBarControls.

CommandBarControls represent a complex hierarchy of objects and subobjects 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). I wish I could draw a nice diagrammatic representation of the object hierarchy, but I'm terrible at such things(honest!), and I'm sure there are such diagrams depicting MS Office CommandBars object model at MSDN.

In our addin, we'd like to add the following UI elements to Outlook

2 toolbar buttons (with bitmaps) in a new toolband.

a new popup menu item (with bitmap) to 'Tools' menu.

First we need to import Office and Outlook typelibraries to our project. To do this open the project's stdafx.h file and add the following #import directive.

Note: You need to change the paths above to match the location where MSOffice has been installed on your system.

Now that we are all set, let's dig into some code. First the toolband and toolbar buttons.

In the Outlook Object Model, the Application object is at the top of the object hierarchy that represents the entire application. Through it's ActiveExplorer method we get the Explorer object that represents the currently active window. Next we'll use the GetCommandBars method to get the CommandBars object that, as you know, is a collection of all of Outlook's toolbands and menuitems. Then, we simply call Add method of the CommandBars collection with relevant parameters to add a new toolband. Adding new buttons to the toolband is as simple as getting the toolband's CommandBarControls collection and then calling it's Add method. Finally we query the buttons for the CommandBarButton object that we'll use to set button styles, and other button properties like caption, tooltip text etc.

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.

With that under our belt, it's F5 time. If everything has gone OK, the project builds successfully and you are about to get a first glimpse of your addin in action(if you haven't already, that is). Since we are going to run Outlook to test our addin, in the 'Executable for Debug' dialog, browse to the current path of Outlook executable (Outlook.exe) and we're finally ready to go. In Outlook goto Tools->Options and under the Other tab, click Advanced Options. From the Advanced Options dialog, click on COM Addins button. Next check the entry for our addin in the Addins Available listbox and click OK. As our addin is loaded a new docked toolband with 2 buttons gets created.Also checkout the cool looking menu item that you just added to Outlook's Tools menu.

There it is!! Not only have you programmed a working Outlook addin, and one that extends Outlook with cool looking toolbars and menu item, thanks to ATL, your < 50Kb addin also qualifies as the leanest, meanest COM server in town. So savour the moment! :)

Lord of the (disp) Sinks

Putting together a couple of toolbars and menu items, by themselves, are not very useful unless we can write command handlers that respond to their events. So let's get right to it. Of course here, on click of the different buttons and menu items, we'll be just popping up simple messageboxes. But this is where you'd code your addins functionality. From CRM tools, automated contact management, mail notification and filtering to advanced document management systems to full fledged applications, COM addins can perform an endless variety of tasks.

The CommandBarButton control exposes a Click event that is triggered when a user clicks a command bar button. We are going to use this event to run code when the user clicks the toolbar buttons or the menu item. For this, our COM object has to implement an event sink interface. For the CommandBarButton control, this event dispinterface is _CommandBarButtonEvents.The Click method is declared as

Thus all we have to do is implement sink interface/s that will be called by the event source through regular connection point protocol whenever a menu or a toolbar button is clicked. What we get for parameters to our callback method is a pointer to the the source CommandBarButton object and a boolean value that can be used to accept or negate the default action. As to implementing a dispatch sink interface, that's nothing new and as an ATL programmer you have probably done this a lot of times.

But for the uninitiated, ATL provides 2 template classes IDispEventImpl<> and IDispEventSimpleImpl<> for ATL COM objects, that provide an implementation of IDispatch. I prefer the light-weight IDispEventSimpleImpl cause it doesn't need the extra typelib info. Just derive your class from IDispEventSimpleImpl<>, set up your sink map, rig up your callback params through _ATL_SINK_INFO structure and finally call DispEventAdvise and DispEventUnadvise to connect and disconnect from the source interface. For our toolbar buttons and menu items, if we were to write a single callback method for all events, then,once we have a pointer to the CommandBarButton that triggered the event, we can use it's GetCaption method to get the button text, based on which we can perform some selective action. For this sample however, we'll have one callback per event.

Here's how the code goes:

Derive your class from IDispSimpleEventImpl- The first parameter is typically the child window ID incase of activeX controls. For us however, this can be any predefined integer that uniquely identifies the event source (in our case the first toolbar button) <>

The sinkmap We'll setup the sinkmap using the ATL macros BEGIN_SINK_MAP() and END_SINK_MAP(),the sink map to be populated by SINK_ENTRY_XXX. The sink map basically provides the mapping between a dispid that defines the event and the member function that handles it.

Now that everything is in place, we have to connect to and disconnect from the event source with DispEventAdvise() and DispEventUnadvise(). Our CAddin class's OnConnection() and OnDisconnection() methods are just the place to do this. The parameters to DispEventAdvise() and DispEventUnadvise() are the any interface on the event source and the desired event source interface respectively.

//connect to event source in OnConnection// m_spButton member variable is a smart pointer to _CommandBarButton// that is used to cache the pointer to the first toolbar button.
DispEventAdvise((IDispatch*)m_spButton,&DIID__CommandBarButtonEvents);
//when I'm done disconnect from the event source//some where in OnDisconnection()
DispEventUnadvise((IDispatch*)m_spButton);

Similarly implement disp sinks for all your command buttons and menu items, write handlers and connect and disconnect from them as described above. That's all there is to it. If everything went without a hitch, after you rebuild and run the addin, whenever the buttons or menu item is clicked, your respective callback method should be executed.

Adding a propertypage

The last thing we'll learn to do in this tutorial is how to add our own 'Options' property page to Outlook's Tools->Options property sheet.

The catch here is that to add a page to Outlook's Tools->Options menu as a part of our addin, our addin as to implement a propertypage as an activeX control. When the user goes to Tools->Options menu item, the Application object fires an OptionsPagesAdd event, as exposed through the _ApplicationEvents dispinterface in the Outlook object model. Extracts from the IDL definition (as described in MSOUTL9.olb viewed through OLE/COM object Viewer) looks like

The OptionsPagesAdd event passes us a pointer to the PropertyPages dispinterface, whose Add method will finally add the page. The parameters to the Add method are the the ProgID of our control and the new tab caption text. Similarly, to remove a page, call Remove() with the index of the targeted page. Now let's get down to the nitty gritty of it.

Begin by adding a new ATL activex composite control to the project through Insert->New ATL Object. Select Controls from category and Lite Composite Control from Objects listbox and click Next. Enter the ShortName as PropPage and in the Attributes tab, check the Support ISupportErrorInfo option. click on OK to accept all the default options.

Now we are going to implement the PropertyPage interface. So in ClassView, rightclick on CPropPage class, choose Implement Interface and click Add Typelib button as before.This time check the Microsoft Outlook 9.0 Object Library and click OK. From the Interfaces listbox check PropertyPage and click OK.

The Wizard adds default implementations for the 3 methods of PropertyPage, Apply(), get_Dirty() and GetPageInfo().Now make the following modifications. In the com map, modify the line

COM_INTERFACE_ENTRY(IDispatch)

to

COM_INTERFACE_ENTRY2(IDispatch,IPropPage)

to resolve any ambiguity. Next to implement IDispatch, we'll use the IDispatchImpl<> template class. So in the CPropPage class declaration replace

Also remove the redundant #import statement at the top of the PropPage.h file. The typelib has already been imported once in stdafx.h, so this is not needed.

Next we are going to connect and disconnect our addin to ApplicationEvents dispinterface and write the callback method. You already know what to do. Again use the IDispEventSimpleImpl<> template to setup the disp sink for ApplicationEvents, update the sink map and write the callback method for OptionsAddPages event as described above. Since we are using IDispEventSimpleImpl<> multiple times, use a typdef for each event interface implementation. Thus the additions are:

Finally in OnConnection() and OnDisconnection(), call DispEventAdvise and DispEventUnadvise to connect and disconnect to ApplicationEvents, as before. Now that we have everything in place, rebuild the project. Next hit F5 and goto Outlook's Tools->Options menu. You should see the new tab that we just added. But hangon, when you click on the new tab, instead of the propertypage, a MessageBox tells us that the propertypage cannot be displayed. What happened? Has all our hard work gone to waste? Fear not!

What is happening is that although the propertypage gets created, Outlook doesn't get any information about the propertypage control's keyboard behavior. The default ATL implementation of IOleControl's GetControlInfo method returns E_NOTIMPL and consequently the container site cannot process any keystrokes for the propertypage or the contained controls.Hence our page is not displayed. To fix this simply override GetControlInfo() to return S_OK.

In the PropPage.h file, add the declaration:

STDMETHOD(GetControlInfo)(LPCONTROLINFO lpCI);

Our overridden method just returns S_OK, so in the PropPage.cpp file, implement GetControlInfo() as

That's it. Now when you build the project and activate the OutlookAddin tab of Options propertysheet,our new propertypage should get displayed without any errors.

With that we come to the journey's end. The list of things you can do in an Outlook2000 or Office2000 addins are endless. Since, in an addin, you have access to the parent applications internal object model, you can do all the things the app does and more. Plus you can also use other interfaces like MS Assistant that are not directly related to any app. The only limit is your imagination!

Happy addin programming. ;-)

Update

April 02,2003

The response this article has generated on CP has led to this little Q & A section. When I posted this, I was not ready for the barrage of questions and comments that have come up. But I feel, this is a good thing. So keep those questions coming!

Some of the topics and code discussed here are applicable for all Office addins.

Handling New Inspector Event

Customize Outlook Menus

Handling New Inspector Event

Q : How do I add custom button/menuitems to Outlook's windows like New->Mail Message window and others?

You can get access to all of Outlook's open windows through Inspector and the Inspectors collection. In particular, InspectorEvent and InspectorsEvents dispinterfaces have a host of events that you should handle. Prominent being InspectorsEvents::NewInspector event, which is fired every time a new Inspector window opens up. It is declared as:

One way to handle the NewInspector event in the addin class is through the ATL IDispatchImpl<> class template and then handle the NewInspector event in the class's overridden Invoke() implementation. We are using IDispatchImpl<> instead of other ATL classes,only for variation. Here is the code you need to add to your project:

Finally we need to Advise and Unadvise to receive the events. This is done, as before, through the ConnectionPoints. OnConnection() and OnDisconnection() is the perfect place to setup the communication. Code to setup the connection goes like :

Provided everything has gone alright, you should be successfully handling the NewInspector event. The article already shows how to add your own menu and toolbar items.So that's it.

Customize Outlook Menus

Q : How do I make an addition to Outlook's standard menuitems like File,Edit etc.?

Once you get Outlook CommandBars collection, you have a to first search for "Menu Bar" CommandBar and then iterate over it's CommandBarControl collection (which contains File,Edit and other menuitems) to get the target menuitem. Next get it's CommandBarControls collection and add your menuitem. Remember that a CommandBarPopup represents a menuitem and CommandBarButton a toolbaritem. You can also use FindControl() if you know Outlook menu ID's.

Here's a tiny little method I call during OnConnection() in my addin, that adds a single menuitem to Outlook's File menu :

Q : How do I add a new menubar say, between Outlook's File and Edit menus?

This is somewhat related to the earlier question about additions to standard Outlook menu items. Again, what we need to do is: from Outlook's CommandBars collection, get the active menubar CommandBar,which represents all of Outlook's menus. Next, make an addition to this CommandBar object, through it's CommandBarControls collection. A menu is a CommandBarPopup object(MsoControlType::msoControlPopup = 10 ), to which we have to add our menuitems i.e. CommandBarButton(MsoControlType::msoControlButton = 1) . Also, in the call to CommandBarControls::Add(), we use the 4th parameter(Before) to specifiy the position as 2(i.e. after File and before Edit)

Like the earlier little function, AddNewMenubar() adds a "Custom" menubar,between File and Edit, and has a single button(menuitem).

References and Acknowledgements

MSKB Articles:Q220600 - How to automate Outlook using VC++Q238228 - Build an Office2000 COM addin in VB Q259298 - Howto use Outlook Object Model through #importQ173604 - How to use CommandBars in Outlook solutionsQ182394- How to use CommandBars in Outlook solutions

Other:Renat Garyaev's (garyaev@acm.org) Vernoter sample.David Mavin, without whom this whole project would not have happened.People at codeproject.com who have egged me on and provided much needed encouragement.All the people who developed the fantastic COM object models behind all Office applications. :)

The 80's prog rock/metal band Queensryche's music.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Amit Dey is a freelance programmer from Bangalore,India. Chiefly programming VC++/MFC, ATL/COM and PocketPC and Palm platforms. Apart from programming and CP, he is a self-taught guitar and keyboard player.

I am trying to customize the Word and Excel Ribbon menus from within a legacy ActiveX control written in MFC only. After registering the Add-in programatically I launch Word from teh ActiveX. I have implemented all of the IUnknown and Dispatch interfaces for IRibbonDisp (my new interface), IDTExtensibility2, and IRibbonExtensiblity, modified my ODL with the new interface (shown below), and wrote code for the various aspects of what I gathered from the ATL examples out there, but the GetCustomUI method is not getting called. Not sure where to turn. Has anyone already done this in C++ without ATL and would be willing to share what they did?

ODL looks like this:

// ifadesktop.odl : type library source for ActiveX Control project.

// This file will be processed by the Make Type Library (mktyplib) tool to
// produce the type library (ifadesktop.tlb) that will become a resource in
// ifadesktop.ocx.

The IDTExtensibility2 method implementations don't do very much but cache a pointer to the _Application interface, but the examples that I saw that used IRibbonExtensibility too didn't do more than that either.

The interface map in the .cpp looks like this (all of the IID's are defined correctly):

I tried playing around with the interface parts above by using the nested class for IRibbonDispObj in place of Dispatch for the two Office interfaces, but this caused the MySaveToDMS method to fire immediately and Word to crash. In this current form, Add-In Spy says the Add-in is loaded, but it is set to "not loaded" because it says there was "a runtime error while loading the COM Addin." What error I am not sure. Anyone know how to find out?

Lastly, I will say that I tried using the ATL example in its own CRibbonDisp class (with the appropriate coclass added to the ODL) which included adding ATL support to the ActiveX Control in the process, and had the menu showing up and capturing the necessary event, but I could not call into any of the legacy ActivX Control's methods form this new ATL driven class. I'd be willing to go back to that route, but I need to know how to delegate the call to MySaveToDMS to the ActiveX Control's interface. A simple QI for it from the CRibbonDisp class failed.

I am wondering if I somehow got caught up with the wrong dispatch pointers, but really am not sure where.

There is a neat problem, when the window is smaller, and toolbar is docked so that some buttons will be accessed via a pop-up menu, these do not work, you will not be notified. How come? How to solve this.

I want to add a function in OutLook's connect people channel ,now it's popmenu can support 'dail' and I hope I can add an popmenu item than can support 'message'. When I click ,it can pop a dialog ,we can select one telephone num
for send message.
thanks .

This is a pretty good article - it explained a lot more than some other articles I've tried reading.

I've almost got an add-in working - using your example I've managed to get a menu item to appear exactly where I want it. The problem is, I can't seem to get the event sink to work. I've re-read this article a dozen times and compared your source code to mine, but I cannot see what I have done wrong.

Here are the relevant parts of my code for the sink interface. Can you see what I have done wrong?

Hey first of all thanks for such a lovely article. It helped me alot in adding the button in Outlook.
Now what I want is to add a button to the New Mail Message Window and attach events to it when that button is clicked. I tried it with using InspectorEvents but didn't worked. Can you please help me out how can I do that?

I had a problem while adding a menu item to Word. The menu item is being added to Word and is not removing after closing the Word (even if I made the Menu item to TEMPORARY). One new Menu Item is being added every time I open the MS Word.
please help to resolve this.