Introduction

After having written about developing Office COM addins in an earlier article, I get a lot of mail from people trying to write Word addins. Through this article we will discuss common development issues regarding Word addins in general. First, we learn how to build a simple WORD 2000 ATL COM addin. Later in the article, we will get down to Word's VBA macro side of things, and write an addin that works for all versions of Word, independent of COM addin support.

I'm assuming you have read my previous article on Office addins and taken a look at the sample project. There are a lot of issues in addin development like adding custom menu/toolbars, handling events, property pages etc that are common for all Office addins, including Word. These have already been discussed in that article. Here we are going to write a Word 2000 COM addin, to begin with. Later, we'll delve into Office and VBA in general, and with respect to with C++ addins.

Writing a Word2000 Addin

To begin, create a new ATL COM Appwizard generated dll project called WordAddin. Next insert an ATL Simple Object called Addin, as in the previous article. Next use the Implement Interface ATL Wizard to implement _IDTExtensibility2. This, as you know, is the at the heart of COM addin support for all Office applications. Next, to program against Word, we need to import Word's typelib. That means, we need to import 3 interdependent typelibs. In your project's stdafx.h add the following code:

Make sure that you change the path to point to the correct locations of the files for your system.

The different locations for the #import statements is needed for the compiler to recognize everything, generate the correct set of wrappers, and compile and link correctly. Moving on, to register your addin with Word2000, add the following code to Addin .rgs registry script file (under FileView->Resource Files) and add the following to the end of the file.

Yes, we'd like our addin to be called 'Word Custom Addin' ('I love CodeProject.com Addin', would have been too blatant!), and loaded when Word starts up. So what else is new? :)

If everything has gone right, you now have a working WORD addin to which you should add your own implementation code. By now you already know how to add buttons and menu items in your addin, property sheets etc; all that has been discussed in the previous article holds true. The only thing different in the Word Object Model, and from the code in the Outlook addin example, is that there is no ActiveExplorer object in Word, and you should get the CommandBars interface directly from Application, the topmost in the object model.

Handling Events

Probably in your addin, you'd also be interested in handling some of Word's events.A case in point is the Application objects DocumentOpen event, with DISPID=4, which is handled here. Word has a complex object model and you will find a host of other such events. If you use the good old OLE/COM Object Viewer to view msword9.olb, you'd find IDL like :

All that remains for us to do is to add the code to setup and break down the connection. Using the ATL template class, therefore all we have to do is call DispEventAdvise() and DispEventUnadvise(). Our CAddin's OnConnection() and OnDisconnection(), needless to say, is the right place for doing this.

Now you have a working Word COM addin template, proudly under your belt. To this you should add your own implementation, error-handling routines etc. Moving on to something different, let's explore VBA side of WORD. Next, I'll discuss a very specific scenario where I use a mix of C++ code and VBA macros in my addin.

Macros and the Visual Basic Editor

I was working on a project, where the COM addin model of things suited us fine. Except the client had a large number of Word97 users, and Word97 has no support for COM addins, specifically the IDTExtensibility2 interface. Basically in my addin I needed to add a few custom menu items and buttons, clicking which usually meant displaying a couple of dialogs. Now, a COM addin would be perfect for Word2000 users. But was there some way we could make the addin Word97 compatible?

This leads us to Visual Basic for Applications(VBA). Most Office developers know that MS Office components and applications support a rich scripting object model and a scripting interface known as VBA. Before Office version 4, each application in the suite was very distinct. For developers, wasn't easy to create integrated solutions using multiple Office applications, because each application had a unique programming environment.

This problem was addressed through MS Visual Basic for Applications (VBA), which made it's debut in Office 95 - albeit, in a few applications. By Office97, every app in the suite supported standard VBA interfaces. The set of functions that a VBA routine, or macro, can use to control its host application is the same set of functions that the OLE Automation client can use to control the application externally, regardless of the programming language for the controller. Word 97 includes Visual Basic 5.0, a sophisticated development environment that is shared across Office applications: Word, Excel, PowerPoint, and Access.

In Word, VBA and Visual Basic goes beyond being merely a macro language—it is a full-featured programming development environment. The Visual Basic Editor (VBE) uses the familiar programming interface of Microsoft Visual Basic 4.0 as a base for creating and editing script code. Through VBA, Word supported everything from macros to addins to document templates. So what is a document template? A document template(*.dot) is simply a document with macro code (script) embedded in it. In Word, macros are persisted in documents and templates as Visual Basic modules. Although macros are ordinarily stored in the user's default template, Normal.dot, Word allows you store and use macros in any document or template. Moreover, any such document template in the Office installation's Startup folder, would be autorun.Additional templates and macros can be loaded using the Templates & Add-ins or Macros dialog box in the Tools menu. Macros and document templates (also supported for WordPerfect) can also be shared across users in a Workgroup.

What is also interesting is, MS Word97 onwards also supports a group of global macros that gets invoked with the host application. These AutoXXX macros like AutoExec(), AutoNew(),AutoOpen(),AutoClose() and AutoExit() gets executed along with the host application, just as their names suggest. This sounds most useful and it is.

How? If we were to create a document template, handle such Auto macros in it, and then create and destroy our addin (which is written as an ActiveX server) through VBA macros - then we should be on the home stretch. This is what IDTExtensibility2 does for COM addins - primarily a way to connect and disconnect to the topmost Application object. So how can we substitute something like this in our Word97 addin? Through document templates. Document templates, by themselves, can be described as VBA addins. Every document template is set up to interact with the Document object associated with the project through the ThisDocument property. We must also remember that all macros implicitly reference the Application object.

Back to the task at hand, suppose we were to write a document template and in it, add code to ourAutoExec() and AutoExit() handlers. In the handlers, we create and release our addin COM class object and subsequently call it's methods. Moreover, our ATL COM class should implement two methods, Init() and Uninit() through which the Application object is set in the C++ addin. Our macros ought to look like:

We can choose to automatically load and unload our template by placing it in the Office's Startup folder.Similarly you can handle AutoClose(),AutoNew() and AutoOpen() macros, which are more document related. For the moment, since I'm handling all button/menu creation and events in my C++ COM object, the communication is unidirectional(i.e. macro->addin), but bidirectional communication is not too difficult.Suppose you have a macro defined in your template called "NewMacro"; one way to trigger this macro from a C++ addin is through Application::Run() or Application::RunOld(), depending on whether you need to pass any parameters. What is interesting is that, you can also trigger the macro through a button click, by setting the OnAction property of the CommandBarButton object of your button or menuitem.

This is just what I did for my project. We have a single solution, consisting of a dll and a dot file, that runs across all versions of Word - from 97 to XP, exposing and encapsulating most of it's functionilty through compiled C++ code. Unfortunately, this VBA support brings up the security issue, and in later versions of Office like XP, the user can determine the security level in macro execution context. Although this was a non-issue with us, you need to be aware.

All of what we have learnt so far. has been espoused in the accompanied VC++ 6.0 Universal Addin project, which I will briefly describe next.

Universal Addin

Universal Addin is a simple ATL/COM AppWizard generated dll project, to which I inserted an ATL COM IDispatch-based Simple Object called Addin. While the name sounds very pretentious, 'Universal' refers to it's ability to run across all versions of Word.

What lends it such universality is the addin.dot Word document template that is a part of the addin, and it's AutoXXX Macros. Our Addin class has two member methods Init() and Uninit() that take a single IDispatch* to the Application object. Our Uninit() implementation is very simple and we could have done without the IDispatchPtr parameter; might not be so for your implementation.In the project, we add a single button through the addin, clicking which triggers a VBA named macro, that in turn calls a method of our COM class. By all means, you can connect to CommandBarButtonEvents and write C++ code to handle button click, as done previously.

Out aim was to write an addin for Word, bypassing the the COM addin architecture and IDTExtensibility2.That concluded, all that remains for me to say is that whatever you can do with VBA, you can do from C++ and vice versa - the magic is in OLEAutomation.

Acknowledgements

Everything I know about Office development, I learnt at MSDN. You, too, can find a hoard of technical articles and short KB code snippets related to everything from Office development to COM and OLE Automation. MSDN rules - as we all know. :)

Thanks to Peter Hauptmann (a.k.a Peterchen) for his comments and suggestions.

History

First revision - 14th April, 2003.

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.

Comments and Discussions

How to override Word's ApplicationEvents4_Event interface events such as quit/DocumentBeforeClose event? Most examles online are using VB or c#， how to handle the Quit or DocumentBeforeClose events in C++, how to override the default handler? Thank you!

Is it possible to get a HWND handle to the CAddin or the ATL simple object? The HWND handle will be used in PostMessage() function as parameter. I tried to derive the CAddin from CWindowsImpl, but failed. Thanks for help!

Dear Amit, once the universal addin is ready how can I automatically install it for every version of Word? As far as I know, there is no way of being sure about what is the startup folder for word 97 (it could have been changed from inside Word options and there is no registry key nor VBA property telling you where it is). Do you know if this is correct?

I'm trying to make an ATL DLL to use in word/VBA for specific purpose.But I ran into a problem I couldn't find the solution on the net so far:for example, i want to have a class Big, and a class Little, and one property of Big would be a Little pointer, and in VBA i want to access methods and properties of those two classes.This seems most simple but I don't know how to do it in MSVC++, I simply don't know how to put an object of a class i have defined as another class' property, and be able to see both in vba.

If toolbar starts from a macro, i have this toolbar on a pane but button doesn't work. If i pressing it Word trowing me error: "The macro cannot be found or has been disabled becose of Macro security settings". However if put the function execution inside Init function (CAddin::ClickItem1();) it executing successfuly, and if i put it on action event of button i getting error. Besides, if i trying to load toolbar from COM Addins i getting another error" "Not loaded. A runtime error occured during the loading of the COM Add-in".

Hi,I am creating an Add-In for PowerPoint. I added new command bar in it. If I open two PowerPoint document the event are triggering two times. For avoiding it I am registering the events only First time but in this case I have another problem.If I open two PowerPoint document simultaneously then the event firs only first time after button click. While if I open the PowerPoint document one by one its working fine. I have tried tag property but its not working.Do anybody have any clue.ThanksAnil Kumar Gupta

I am working in VC++ and want to write a word addin and i m having Office2000. I got some code help from codeproject and its your article i go through that instruction i got some errors and warnings that are exactly stated below as they come to me.I had edited some code In Addin.h ==>#import "C:\\Program Files\\Microsoft Office\\Office\\MSWORD9.olb" rename_namespace("MSWORD"), rename("ExitWindows","WordExitWindows"), named_guids, raw_interfaces_onlyusing namespace MSWORD;

#import "C:\\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.olb" rename_namespace("VBE6")using namespace VBE6;and these three files are avilable on my programe files folder.i extreemly need your help. either you can mail me some source code in zip format or give me the full flagged instruction for that. My System Configuration is Windows 2000 Office 2000 P4. i will be very thankful to you. Reply me as soon as possible.

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.

I don't know if its the same problem but for what its worth I had similar experience. Each time I opened Word I got a new button on the menu. The problem was that the code (auto generated by .net) first checks if the menu item exists and if it does not it adds a new one. My problem was that i edited the default name of the button and mis-spelled it when setting the caption

Did you ever get to the bottom of this? I've got a menu that gets created with AutoExec from a .dot file and I find all my menu keeps on getting copied into the Normal.dot file which is making this file huge and slowing down the opening of word.

I've got a word addin created in C# which creates a custom property. When the document is first created it puts in the custom property as expected and saves it. When you open same document and change the custom property, word doesn't pick up that the document has been changed and doesn't save the custom property even if in my code I call Doc.save().

i made some path changes in ur code (.olb,dll) according to my installation folders. i m using OFFICE 2003 MS Windows 2000 Family Server. when i compiled the file. it produced the following errors. i tried my best to find out the solution within a limited time but couldn't. plz do help me as soon as possible.

I was getting same errors. I think he built this with an earlier version of dev studio than 2003. To fix it in VS2003, I did the following:- removed variable m_spButton - it is not used anywhere - he probably left that in accidentally- replace CommandBars with _CommandBars everywhere- Similarly replace other classes with underscored version - The _NoAddRefReleaseOnCComPtr errors went away after that.Per

hi Amit,i have used ur office 2000 addin and it works fine.but when i open new window through new button, i get the buttons in the toolbar.im creating button and a edit control.but when i click the button nothing happens.please tellme how to capture the event and how to handle it.

I've succesfully completed the previous article on an Outlook add-in with no problems. However, at the early stage of writing this add-in, where i check whether Word can see my addin, it can't. I've checked the registry using regedit, and the correct details for this add-in are there, right next to an add-in that *does* show up in word (a default one, something todo with web pages). even when i point word straight to the .dll in my vc++6 project folder, it won't see it. any ideas? the code is as per the article, without filling in any member functions for the add-in, or putting in any buttons etc. I'm using word 2000.thanks for any help!

I had this at first...and then I found out that there is not a problem! Well, there is, but the add in is there! To see it, do the following:Tools -> CustomizeSelect the Commands TabSelect Tools in the CategoriesDrag the "COM Add-Ins" to a toolbar somewhere.Click Close.

Now if you click the new button you just added, it should be listed!

The second thing to realize is that after you do the basics of the artical (and nothing beyond that), the DLL does nothing!! So you won't see anything.

1) Does anyone know how a menuItem can be added to Word->File->SendTo menu?

2) I did input the code to add a menuItem from the "outlook example" at http://www.codeproject.com/com/outlookaddin.aspbut the menuItem is not being removed when I close WordDoes anyone have an idea why that happens and how this can be fixed?

//we'd like our new menu item to look cool and display // an icon. Get menu item as a CommandBarButton CComQIPtr < Office::_CommandBarButton> spCmdMenuButton(spNewMenu); ATLASSERT(spCmdMenuButton); spCmdMenuButton->put_Style(Office::msoButtonIconAndCaption);

// we want to use the same toolbar bitmap for menuitem too. // we grab the CommandBarButton interface so we can add // a bitmap to it through PasteFace(). spCmdMenuButton->PasteFace(); // show the menu spNewMenu->put_Visible(VARIANT_TRUE); m_spMenu = spCmdMenuButton;// hr = CommandMenuEvents::DispEventAdvise((IDispatch*)m_spMenu); if(FAILED(hr)) return hr;

1) Does anyone know how a menuItem can be added to Word->File->SendTo menu?

2) I did input the code to add a menuItem from the "outlook example" at http://www.codeproject.com/com/outlookaddin.aspbut the menuItem is not being removed when I close WordDoes anyone have an idea why that happens and how this can be fixed?

//we'd like our new menu item to look cool and display // an icon. Get menu item as a CommandBarButton CComQIPtr < Office::_CommandBarButton> spCmdMenuButton(spNewMenu); ATLASSERT(spCmdMenuButton); spCmdMenuButton->put_Style(Office::msoButtonIconAndCaption);

// we want to use the same toolbar bitmap for menuitem too. // we grab the CommandBarButton interface so we can add // a bitmap to it through PasteFace(). spCmdMenuButton->PasteFace(); // show the menu spNewMenu->put_Visible(VARIANT_TRUE); m_spMenu = spCmdMenuButton;// hr = CommandMenuEvents::DispEventAdvise((IDispatch*)m_spMenu); if(FAILED(hr)) return hr;