Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Event Sinks

0.00/5 (No votes)
1 Mar 2007 1  
An introduction to event sinks in C++ in the context of ATL COM Add Ins.

Contents

Introduction
Type Library Files
Three New Projects
Apply The Kludge
Connect To Microsoft Word
Modify The Microsoft Word Main Menu
Add The Event Sink
Create A New Dialog
Compile And Install
Deployment

Introduction

(Download the WordFindFacility source files at the top of the article)

COM friendly applications like the Microsoft Office suite of programs have connection points that allow detection of events by external processes. What sort of events may be detected are of course entirely dependent on what the vendor of the host application is willing to provide. However, such events may include things like the selection of a menu option, the opening and closing of a document or the opening or closing of the whole application. Event sinks are used by client processes to detect these events. As mentioned in the article ATL COM Shared Add Ins Using C++ - The Easy Way, event sinks are the elegent method of detecting the selection of a menu item that has been added to the host application by the Add In itself.

The topics of Connection Points and Event Sinks are included in many excellent books including Professional ATL COM Programming, by Dr Richard Grimes, Wrox Press, 1998, details of which are available at http://www.grimes.demon.co.uk/books/prof_atl.htm. I do not intend to paraphrase the content of these texts here. My intention is to present event sinks in the context of ATL COM Add Ins and the ATL COM Shared Add Ins Using C++ - The Easy Way article. To do this I will basically redo the WordFindFacility project example. Once again, this example uses Microsoft Visual Studio 2005 and Microsoft Word 2003. To readers that have already done this example in the ATL COM Shared Add Ins Using C++ - The Easy Way article, I warn against attempting to modify existing code. COM Interfaces once defined and assigned guids tend to hide in every dark corner like your worst nightmare.

Type Library Files

(Downloads modified Microsoft Office Type Library files at the top of this article)

The event sink process typically involves the use of a tlb type library file. However, tlb files are not usually supplied with applications like Microsoft Word. Fortunately, the OLE/COM Object Viewer described in the ATL COM Shared Add Ins Using C++ - The Easy Way does create idl files and a tool called MIDL described later, does compile idl files to tlb files. Unfortunately, the compilation of idl files generated by the OLE/COM Object Viewer is not always successful.

The WordFindFacility project does require a Microsoft Office tlb file, so create a new folder somewhere and make a copy of the file MSO.dll typically found in the folder "C:\Program Files\Common Files\Microsoft Shared\OFFICE11". To create an idl file, go to the "Common7\Tools\Bin" folder within the Visual Studio folder and fire up OleView.exe. Press the toolbar button with the three red triangles and open the saved MSO.dll file. In the ITypeLib Viewer window save the file as MSO.IDL in the same folder as the saved MSO.dll file. Close the OLE/COM Viewer.

The next step is to open a Command Prompt window. The Command Prompt window provided by Visual Studio is preferable because it has the environment variables set up to suit command line tools like MIDL. To use the Visual Studio Command Prompt window, press the Windows Start button. Then select All Programs, Microsoft Visual Studio 2005, Visual Studio Tools and Visual Studio 2005 Command Prompt. Navigate your way to the folder containing the MSO.dll and MSO.idl files.

Once in the correct folder you can attempt to compile your MSO.idl file into a MSO.tlb file by entering the following command:

midl MSO.idl

This compilation will predictably fail. Now it is a case of opening the MSO.idl file with Notepad or your favourite text editor and debugging the code. The first course of action is to delete the custom declaration at line 11. Earlier versions of Microsoft Office did not have this line. Next, add the following lines straight after the importlib declaration near the top of the library Office block:

typedef float single;
coclass CommandBarComboBox;
coclass CommandBarButton;

Then change the line in the interface Scripts : _IMsoDispObj block from this:

[in, optional, defaultvalue(<unprintable IDispatch*>)] IDispatch* Anchor, 

To this:

[in, optional, defaultvalue(0)] IDispatch* Anchor, 

As it has been in earlier versions of Microsoft Office.

Now move the group of typedef statements commencing with MsoOrgChartOrientation and concluding with MsoDiagramNodeType from their current location, to the end of the group of typedef statements nearer the beginning of the library Office block. The last typedef in this earlier block is MsoFeatureInstall. Finally, move the group of typedef statements commencing with MsoAlertButtonTypeand concluding with MsoTargetBrowser to follow straight on from MsoDiagramNodeType.

At this point the mso.idl file should compile with quite a lot of warning messages. It should be noted that this process of editing and compiling is something of a compromise. Clearly if you are not intending to use the particular interfaces that are difficult to compile, then you need not worry too much. However, the purpose here is to create a tlb file that can potentially be used on a number of projects, so some effort to get things right is warranted.

Three New Projects

As discussed in the Introduction to this article, the example from the earlier article ATL COM Shared Add Ins Using C++ - The Easy Way will be redone using event sinks. The first thing to do then is create a new Shared Add In project. So fire up Visual Studio 2005 and start a new project. In the New Project dialog, the Project type is Other Project Types and Extensibility. The template is Shared Add In and use the project name "WordFindFacility". On page 1 of the Shared Add In Wizard, Create an Add-in using Visual C++/ATL. On page 2 the host application is Microsoft Word (untick the rest). On page 3 make "Word Find Facility" the name and description. On page 4 tick both I would like my Add-in to load when the host application loads and My Add-in should be available to all users of the computer it was installed on, not just the person who installs it. On page 5 press Finish.

If you haven't already done so, now is the time to create two MFC projects as described in The MFC Way of the article ATL COM Shared Add Ins Using C++ - The Easy Way. One of these projects is of course to generate COM wrappers for the Microsoft Word interface. The other is for the common Microsoft Office interface. In both cases the project is an MFC Application where the application type is a Single document and the Automation tick box has been ticked on the Advanced Features page. In each of these projects select Project and Add Class from the main menu. The class to be added is a MFC Class From TypeLib. In the Add Class From TypeLib Wizard dialog, use the radio buttons to select Add class from File and press the "..." button to locate the relevant file. For Microsoft Word the file is MSWORD.OLB typically in the folder "C:\Program Files\Microsoft Office\OFFICE11". For Microsoft Office the file is MSO.dll typically in the folder "C:\Program Files\Common Files\Microsoft Shared\OFFICE11". In both cases press the ">>" to add all class.

Apply The Kludge

COM Interface wrappers created, the following files need to be copied across from the MFC Project containing the Microsoft Word COM interface wrappers into the new WordFindFacility project:
  • CApplication.h
  • CDocument0.h
  • CWords.h
  • CRange.h
Also, the following files need to be copied across from the MFC Project containing the Microsoft Office COM interface wrappers into the new WordFindFacility project:
  • CCommandBarPopup.h
  • CCommandBarButton.h
  • CCommandBar0.h
  • CCommandBars.h
  • CCommandBarControl.h
  • CCommandBarControls.h
Finally, the files DotDispatchDriver.h, DotDispatchDriver.cpp, DotOleErrors.h and DotOleErrors.cpp, created in The Kludge section of the ATL COM Shared Add Ins Using C++ - The Easy Way article need to be copied across into the new WordFindFacility project. Once the files have been physically copied into the WordFindFacility folder, they must also be added to the project by performing a right mouse click over the WordFindFacility tree node in the Solution Explorer in Visual Studio. In the popup menu select Add and Existing Item, then select the files using the Add Existing Item dialog. Now to complete the transition from MFC to ATL all occurs of the variable name COleDispatchDriver must be found and replaced by the variable name CDotDispatchDriver. Also, every header file containing a reference to CDotDispatchDriver must also contain the following lines near the top of the file:

#pragma once
#include "DotDispatchDriver.h"

While near the top of these header files, the line commencing #import must be removed. If you have been following the text diligently, it might be wise to try that first compile at this point.

Connect To Microsoft Word

With a happy compiler it is at last time to start typing C++. Firstly, some sort of mechanism for calling into Microsoft Word is required. Referring to the wizard generated Connect.cpp source file in the WordFindFacility project, there is a method called OnConnection that is passed an IDispatch pointer that serves the purpose nicely. So in the Connect.h header file add the following line amongst the other include declarations:

#include "CApplication.h"

then add the following lines to the CConnect class declaration:

private: 
    CApplication m_oWordApp; 

Now going back to the Connect.cpp source code, add the following lines to the OnConnection method discussed earlier:

try
{ 
    m_oWordApp.AttachDispatch(pApplication); 
}
catch( DotOleException* e )
{ 
    DotOleErrors::OleErrorMessages(e); 
}
catch( DotOleDispatchException* e )
{ 
    DotOleErrors::DispatchErrorMessages(e); 
}

Also the following line must be added amongst the other include declarations:

#include "DotOleErrors.h"
What has been done here is of course the putting of a wrapper around the IDispatch pointer. However, when using this stuff it must be kept in mind that despite appearances, interprocess communication is going on here so frequent use of try/catch statements is advisable.

Modify The Microsoft Word Main Menu

The next job is to modify the Microsoft Word main menu to include our word find facility. This could be a case of adding a menu item to an existing menu bar option, but for this example a new menu bar option will be added as well. So in the Connect.h header file add the following lines amongst the other include declarations:

#include "CCommandBarPopup.h"

#include "CCommandBarButton.h"



then add the following lines to the CConnect class declaration:

private:
    CCommandBarPopup m_oWordFindPopup;
    CCommandBarButton m_oWordFindButton;

The two variable declarations thus added represent the new menu bar option and the new menu item respectively. Whilst in the Connect.h file the following lines can also be added to CConnect class:

private:
    void AddMenuItems();

    void RemoveMenuItems();

These are of course the declarations for the methods that will add and remove the menu items for the word find facility. Switching back to the Connect.cpp file, the new method AddMenuItems is added to the end of file, commencing with the following lines:

void CConnect::AddMenuItems()
{
    try
    {
        CCommandBars oCommBars = m_oWordApp.get_CommandBars();
        CCommandBar0 oMenuBar = oCommBars.get_ActiveMenuBar();
        CCommandBarControls oMenuBarControls = oMenuBar.get_Controls();

        long count = oMenuBarControls.get_Count();
        for (long iii=1; iii<=count; iii++)
        {
            CComVariant vLong(iii);
            CCommandBarControl oControl = oMenuBarControls.get_Item(vLong);
            if ( oControl.get_Caption().Compare( "&Window" ) == 0 )
            {
                MsoControlType type = msoControlPopup;
                m_oWordFindPopup = oMenuBarControls.Add( CComVariant(type),
                                 vOpt, vOpt, CComVariant(iii), vOpt);
                m_oWordFindPopup.put_Tag("WordFindFacility Popup");
                m_oWordFindPopup.put_Caption("&My Menu");
                m_oWordFindPopup.put_Visible(TRUE);
                m_oWordFindPopup.put_TooltipText(
                                    "A Fairly Useless Word Find Facility");
                break;
            }
        }

What is happening here is the Microsoft Word main menu bar control is obtained and each menu bar option is checked in turn to locate the standard Window option. A new menu bar option is then inserted ahead of the Window option. A number of properties are set for new menu bar option including the caption My Menu. Now of course the menu item itself must be added. This is done with the following code which is appended to the code for the AddMenuItems method shown above.

CCommandBarControls controls = m_oWordFindPopup.get_Controls();


MsoControlType type = msoControlButton;
m_oWordFindButton = controls.Add( CComVariant(type), vOpt, vOpt,
                                  CComVariant(1), vOpt);
m_oWordFindButton.put_Tag("WordFindFacility Popup");
m_oWordFindButton.put_Caption("&Find...");

m_oWordFindButton.put_Visible(TRUE);
m_oWordFindButton.put_TooltipText("A Fairly Useless Word Find Facility");
MsoButtonStyle style = msoButtonCaption;

m_oWordFindButton.put_Style(style);
m_oWordFindButton.put_OnAction( "!<WordFindFacility.Connect>" );

Here a menu item with the caption Find... is added to the newly created menu bar option. To finish off the AddMenuItems method the catches for the mandatory try are appended to the end as follows:

}
catch( DotOleException* e )
{
    DotOleErrors::OleErrorMessages(e);
}
catch( DotOleDispatchException* e )
{
    DotOleErrors::DispatchErrorMessages(e);
}

The code for the RemoveMenuItems method is as follows:

void CConnect::RemoveMenuItems()
{
    try
    {
        CCommandBars oCommBars = m_oWordApp.get_CommandBars();
        CCommandBar0 oMenuBar = oCommBars.get_ActiveMenuBar();
        CCommandBarControls oMenuBarControls = oMenuBar.get_Controls();

        long count = oMenuBarControls.get_Count();
        for (long iii=1; iii<=count; iii++))
        {
            CComVariant vLong(iii);
            CCommandBarPopup oPopup = oMenuBarControls.get_Item(vLong);
            if ( oPopup.get_Caption().Compare( "&My Menu" ) == 0 )
            {
                CCommandBarControls oPopupControls = oPopup.get_Controls();
                while ( oPopupControls.get_Count() > 0 )
                {
                    CCommandBarButton button = oPopupControls.get_Item(
                                                   CComVariant((long)1) );
                    button.Delete(vOpt);
                }
                oPopup.Delete(vOpt);
                count--;
            }
        }
    }
    catch( DotOleException* e )
    {
        DotOleErrors::OleErrorMessages(e);
    }
    catch( DotOleDispatchException* e )
    {
        DotOleErrors::DispatchErrorMessages(e);
    }    }
}

This method is not unlike the AddMenuItems method. Once again the control to the Microsoft Word main menu bar is obtained and a search is conducted along the menu bar. In this case however, occurrences of the menu option My Menu is the search target. If any are found, the menu bar option and the menu items beneath it are deleted. This method has been written fairly defensively. It is possible for the same menu option to appear multiple times when things go horribly wrong. So a comprehensive clean up is wise.

Clearly the following include declarations must appear near the top of the Connect.cpp source file amongst the other include declarations to account for the extra data types used in the methods AddMenuItems and RemoveMenuItems:

#include "CCommandBar0.h"

#include "CCommandBars.h"

#include "CCommandBarControl.h"

#include "CCommandBarControls.h"

Obviously the methods AddMenuItems and RemoveMenuItems are not going to have any affect unless they are called as part of the Add In process. So add the following lines to the wizard generated method OnStartupComplete in the Connect.cpp source file:

RemoveMenuItems();
AddMenuItems();

Then add the following line to the wizard generated method OnBeginShutdown:

RemoveMenuItems();

Add The Event Sink

Now is the time to copy that MSO.tlb file created in the section Type Library Files of this article, into the WordFindFacility folder generated by Visual Studio. Having done that add the following lines to the Connect.h file straight after the include declaractions:

#import "mso.tlb" \
    no_namespace, named_guids, raw_interfaces_only, raw_native_types
static const int ANY_NUMBER = 99;

As the name implies the variable ANY_NUMBER can be any number at all. The important point is that it must never change. Straight after the previously added lines add the following:

class CConnect;

typedef IDispEventImpl<ANY_NUMBER, CConnect, &DIID__CommandBarButtonEvents,
                    &LIBID_Office, 2, 3> FindMenuItemEventImpl;

Here an ATL template has been used to define a class called FindMenuItemEventImpl. The parameters "CommandBarButtonEvents" and "Office" and their respective uids are defined in mso.tlb, as can be seen in the mso.idl file. The numbers "2" and "3" are the Microsoft Office version numbers, declared near the top of the mso.idl file. Now the FindMenuItemEventImpl class needs to be one of the classes that CConnect class is derived from, so change the list of classes that CConnect class is derived from this:

public CComObjectRootEx<ccomsinglethreadmodel />,
public CComCoClass<CConnect, &CLSID_Connect>,
public IDispatchImpl<AddInDesignerObjects::_IDTExtensibility2,
       &AddInDesignerObjects::IID__IDTExtensibility2,
       &AddInDesignerObjects::LIBID_AddInDesignerObjects, 1, 0>

To this:

public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CConnect, &CLSID_Connect>,
public IDispatchImpl<AddInDesignerObjects::_IDTExtensibility2,
                 &AddInDesignerObjects::IID__IDTExtensibility2,
                 &AddInDesignerObjects::LIBID_AddInDesignerObjects, 1, 0>,
public FindMenuItemEventImpl

To create a sink map, add the following lines after the COM map declarations within CConnect class in the Connect.h file:

BEGIN_SINK_MAP(CConnect)

END_SINK_MAP()

At this stage it might be reasonable to expect the project to compile. Unfortunately, it is a case of close but no cigar. There are a few name clashes to attend to. Two of these are easily fixed by changing the "import" declaration entered earlier to this:

#import "mso.tlb" \
   no_namespace, named_guids, raw_interfaces_only, raw_native_types, \
   rename("RGB","RGB2"), rename("DocumentProperties","DocumentProperties2")

One of these name clashes is not quite so easy. For this one it is necessary to return to the mso.idl file created in the Type Library Files section of this article. Open this mso.idl file with your text editor and replace every occurence of the string "IAccessible" with the string "IAccessible2". Recompile using the MIDL tool and copy the resulting mso.tlb file across to the WordFindFacilty project folder. Now the WordFindFacility project will compile.

The time has come to specifically identify the Microsoft Office event to be used. This can be done using a text editor and the mso.idl file or the OLE/COM Object Viewer on the mso.dll file. Either way you will find CommandBarButtonEvents defined as follows:

interface ICommandBarButtonEvents : IDispatch {
    [id(0x00000001), helpcontext(0x00038271)]
    void Click( [in] CommandBarButton* Ctrl, 
                [in, out] VARIANT_BOOL* CancelDefault);
};

Such event definitions can contain any number of events, but clearly in this case there is only one, namely "Click" with an id of 0x00000001. Whilst on the subject, it might be noted that MSO.dll contains many event definitions. Indeed, MSWORD.OLB and all of the Microsoft Office suite of type libraries contain even more event definitions. So the list of events that may be detected really is quite comprehensive. Anyway, getting back to the Click event with the id of 0x00000001, using the information thus obtained the following entry is added to the sink map block in the Connect.h file:

SINK_ENTRY_EX(ANY_NUMBER, DIID__CommandBarButtonEvents, 0x00000001, 
              OnFindMenuItemClick))

The OnFindMenuItemClick method is defined in the CConnect class by adding the following lines within the CConnect class in the Connect.h file:

public:
    void _stdcall OnFindMenuItemClick(LPDISPATCH button, 
                                      VARIANT_BOOL* cancelDefault);

Note that the parameters passed into the OnFindMenuItemClick method must be compatible with the parameters defined for the CommandBarButtonEvents Click event in the MSO.idl file. Of course the OnFindMenuItemClick method needs some executable code, so append the following lines to the Connect.cpp source file:

void _stdcall CConnect::OnFindMenuItemClick(LPDISPATCH button, 
                                            VARIANT_BOOL* cancelDefault)
{
}

Now you won't actually receive any events unless you tell Microsoft Word that you want to receive events, so to the end of the AddMenuItems method in the Connect.cpp source file add the following lines:

HRESULT hr;

hr = FindMenuItemEventImpl::DispEventAdvise( m_oWordFindButton );
if (FAILED(hr))
{

    DotOleErrors::HRErrorMessages(hr,"DispEventAdvise FAIL");
}

The variable m_oWordFindButton is of course the newly created Find... menu item on which events are to be received. The following lines also need to be added to the beginning of the RemoveMenuItems method:

HRESULT hr;

if ( m_oWordFindButton != NULL )
{
    hr = FindMenuItemEventImpl::DispEventUnadvise( m_oWordFindButton );
    if (FAILED(hr))
    {
        DotOleErrors::HRErrorMessages(hr,"DispEventUnAdvise FAIL");

    }

    m_oWordFindButton.ReleaseDispatch();

}

This is a lot more than some polite housekeeping. Microsoft Word will not close down unless all such event sinks have been released. Remember to check Windows Task Manager occasionally to ensure that all is well. Anyway the above code requires that the variable m_oWordFindButton is intially set to NULL, so to the CConnect constructor in the Connect.cpp source file, add the following line:

m_oWordFindButton = NULL;

Create A New Dialog

Right, so far we have a new menu item in Microsoft Word that is doing not very much. Time to create a dialog. So fire up Visual Studio and right click on the WordFindFacility project in the Solution Explorer window. From the popup menu select Add and Class.... In the Add Class dialog select ATL from the tree view and ATL Dialog as the template. Press Add, type in "WordFind" as the short name and press Finish. On a personal note, I find the habit of some of the ATL wizards of putting the executable body of class methods in header files extremely irritating. So much so that the first thing I do at times like this is a bit of cutting and pasting to put things right. I encourage you to get into WordFind.h and WordFind.cpp and do likewise - method declarations only in header files. Now switch from the Solution Explorer to the Resource View and in the tree view, double click on the dialog with the label IDD_WORDFIND. Work with the resource editor until you end up with something like the following:

The Close button is simply the default OK with a modified caption. The Find button is new and has the ID IDC_FIND which is set using the Properties window. Whilst in the Properties window click on the lightning button and add a method to handle the BN_CLICK event for the Find button. The default method name is fine. The edit control is given the ID IDC_WORD using the Properties window.

Now to some code for the new dialog. Firstly, ATL does provide a wrapper for its dialog controls which will be used here on the edit control. So add the following lines to CWordFind class in the WordFind.h header file.

private:
    CWindow m_cWord;

Then add the following line to the OnInitDialog method which I trust is now in the WordFind.cpp source file.

m_cWord.Attach( GetDlgItem( IDC_WORD ) );

The Microsoft Word document to be searched will be passed into the CWordFind class during construction, so amongst the other include declarations near the top of the WordFind.h file add the following line:

#include "CDocument0.h"

In the same file, modify the CWordFind constructor declaration to appear as follows:

CWordFind( CDocument0* poDocument );;
Also in the same file, add the following lines to WordFind class:

private:
    CDocument0* m_poDocument;

Finally, modify the constructor code which I trust is now in the WordFind.cpp source file to appear as follows:

CWordFind::CWordFind( CDocument0* poDocument )
{
    m_poDocument = poDocument;
}

During the course of the word searching process, variables representing an index to the current word and the total number of words need to be maintained. Accordingly, the following line needs to be added amongst the other include declarations near the top of the WordFind.h header file:

#include "CWords.h"

Then the following lines of code need to be added to the CWordFind class in the same file.

private:
    long m_lTotalNumberOfWords;
    long m_lWordIndex;
    CWords m_oWords;

Finally the following lines need to be added to the CWordFind class constructor in the WordFind.cpp source file:

m_lWordIndex = 1;
m_oWords = poDocument->get_Words();
m_lTotalNumberOfWords = m_oWords.get_Count();

What is happening here is fairly straight forward. An object is obtained from the supplied document that represents the words in the document. The number of words is then obtained from the "Words" object.

Before moving on to the word find routine itself another include declaration is required. This time in the WordFind.cpp source file, the following line needs to be added amongst the other include declarations:

#include "CRange.h"

Now to the business of the word search. Add the following lines to the OnBnClickedFind method in the WordFind.cpp source file:

CString text;

m_cWord.GetWindowText( text );

while ( m_lWordIndex <= m_lTotalNumberOfWords )

{


    CRange oRange = m_oWords.Item( m_lWordIndex );
    m_lWordIndex++;
    CString word = oRange.get_Text();
    word.TrimRight();
    if ( text == word )
    {
        oRange.Select();
        return 0;
    }
}
::MessageBox( NULL, text + " Not Found", "Not Found", MB_OK|MB_ICONSTOP );

The first thing that happens here is the text retrieved from the edit control. Then the word index is used to step through the document and the word referenced by each index value is retrieved and compared to the text from the edit control. If a match is found the word is selected so that it turns black. An error message is given if the document is passed throught without finding a single match. One might say that the code as presented isn't hard to understand, but working out exactly which commands to use in the first place certainly is. The only advice I have in this regard is given in the Help On Host COM Interfaces section of the ATL COM Shared Add Ins Using C++ - The Easy Way article.

Of course, this dialog needs to be called up when the new Find... menu item is selected so add the following lines to the OnFindMenuItemClick method in the Connect.cpp source file:

try
{
    CDocument0 oActiveDocument = m_oWordApp.get_ActiveDocument();
    CWordFind dlg( &oActiveDocument );
    dlg.DoModal();
}
catch( DotOleException* e )
{
    DotOleErrors::OleErrorMessages(e);
}
catch( DotOleDispatchException* e )
{
    DotOleErrors::DispatchErrorMessages(e);
}

This code requires some additional include declarations, so near the top of the Connect.cpp source file amongst the other include declarations, add the following lines:

#include "WordFind.h"

#include "CDocument0.h"

By now my seemingly excessive use of local variables to define COM wrapper classes may have been noted. Anyone with COM experience will have heard of reference counters. A reference counter determines the lifetime of a COM object. The COM wrappers described in this article do a pretty good job of hiding all this reference counter stuff. However, the existence of reference counters cannot be totally ignored. One must therefore maintain very tight control over the construction and destruction of COM wrapper classes. I find the generous use of local variables to be a fairly painless way of achieving this tight control. When developing Shared Add Ins, it pays to occasionally check the Windows Task Manager to ensure that the host application is in fact closing down when you believe it should be closing down. If close downs are not occurring predictably and you have been using COM wrapper classes religiously, then check that you understand the life cycle of each and every COM wrapper class instance.

Compile And Install

To compile and install the Add In proceed as follows:
  1. Compile the WordFindFacility project. To do this perform a right button mouse click over the project name WordFindFacility in the Solution Explorer window and select Rebuild
  2. Compile the WordFindFacilitySetup project. To do this perform a right button mouse click over the project name WordFindFacilitySetup in the Solution Explorer window and select Rebuild
  3. Install the Add In. To do this perform a right button mouse click over the project name WordFindFacilitySetup in the Solution Explorer window and select Install
Now if you fire up Microsoft Word, type in some text and select My Menu and Find..., your new word find facility should be operational. At this point I should mention that if you end up with a menu bar option that won't go away even when you uninstall the Add In, the procedure to get rid of it is as follows. Firstly, in Microsoft Word select Tools and Customize.. from the main menu. Then simply grab the unwanted menu bar option using the left mouse button and drag it across to the displayed Customize dialog and close the dialog.

Deployment

Now that you have successfully created an Add In you will know doubt want some way of distributing it. During the course of project development, installing the Add In has been a simple case of a right mouse click over WordFindFacilitySetup and selecting Install. However, if you have a look inside the WordFindFacilitySetup folder that was created by Visual Studio as part of the project, you will find that WordFindFacilitySetup is a project on its own, complete with its own Debug and Release folders. In the Debug or Release folder is the WordFindFacilitySetup.msi file which is pretty much all you need to install the Add In. An msi file is of cource a Windows Installer database file, which in this case actually has the executable code for the Add In embedded within it. Incidently Microsoft actually has a tool for creating and editing msi files called orca.exe. Details may be found at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/orca_exe.asp

Getting rid of an Add In requires the use of Add or Remove Programs accessed via the Start button and Control Panel

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.

A list of licenses authors might use can be found here