Writing a COM Add-In Using Visual Studio
Writing a class that implements IDTExtensibility2 is not particularly difficult, but setting up the registry settings for the application you are targeting and creating the setup package for the COM add-in can be tricky. Luckily, Visual Studio provides a wizard that makes writing COM add-ins considerably easier. The wizard creates two projectsone for implementing the COM add-in and a separate setup project for the COM add-in. The COM Add-In Wizard has actually been part of Visual Studio since version 7.0, but you might not have come across it because it is somewhat hidden in the project hierarchy and listed as a "Shared Add-in" project.The wizard can be found under Other Project Types > Extensibility > Shared Add-in and is shown in Figure 23-6. The only clue that the Shared Add-in project might have something to do with Office is the Office icon included on the Shared Add-in icon.
Figure 23-6. Creating a Shared Add-in project in Visual Studio.
[View full size image]

Figure 23-7. Selecting the application host.

Figure 23-8. Setting a name and description for a COM add-in.

Figure 23-9. Setting load behavior for the COM add-in.

Figure 23-10. The Solution Explorer view of a default COM add-in solution.

Changing the COM Add-In Project to Be More Office-Specific
The COM Add-In Wizard will create a project for any application that supports IDTExtensibility2, and as a result it creates a very generic project. The whole point of writing a COM add-in is to integrate with a particular Office application, so the first thing you need to do is add the appropriate primary interop assembly (PIA) for the application the COM add-in is targeting. The COM add-in being built in this example will load into Microsoft Word, so it needs to have a reference to the Word PIA. You will then be able to cast the application object passed in OnConnection to the Microsoft.Office.Interop.Word.Application object defined in the Word PIA.Adding the PIA for Word to a project is quite straightforward; it is just a matter of adding the reference to the Microsoft Word 11 Object Library. Right-click the WordAddin project node in the Solution Explorer tree view and choose Add Reference. Doing so brings up the Add Reference dialog shown in Figure 23-11. Click the COM tab, and then select the Microsoft Word 11 Object Library from the list. Finally, click OK to add a reference to the Word PIA to your Visual Studio project.
Figure 23-11. Adding a reference to the Word PIA.

Listing 23-3. Strongly Typing applicationObject and addInInstance
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode,
object addInInst, ref System.Array custom)
{
applicationObject = application as
Microsoft.Office.Interop.Word.Application;
addInInstance = addInInst as
Microsoft.Office.Core.COMAddIn;
}
private Microsoft.Office.Interop.Word.Application applicationObject;
private Microsoft.Office.Core.COMAddIn addInInstance;
Setting the Start Action
The COM add-in is now almost ready to go. The last problem to solve is that the project is set to start up a new instance of Visual Studio rather than Word. This is easily solved by changing the debug settings for the project so that the project will start up winword.exe rather than devenv.exe. To do this, bring up the properties for the project by double-clicking the Properties project item in the Solution Explorer window, and then select the Debug tab. Doing so brings up the dialog with a Start Action section, as shown in Figure 23-12.
Figure 23-12. The Start Action section of the Debug tab.
[View full size image]

My COM Add-In Project Doesn't Work AnymoreWhat Happened?
A common issue that occurs in COM add-in development goes like this. "I just pressed F5 on my COM add-in project and nothing happens! My COM add-in doesn't appear to load. What's the deal?" Office has a system to protect itself from COM add-ins that fail. When you understand the system, you will better understand how to protect against your COM add-in not loading.Office automatically disables a COM add-in if it detects that it crashed the host application while starting up. When the Office application loads and starts a COM add-in, it puts a sentinel in the registry associated with the COM add-in that it is loading. It then calls the COM add-in's OnConnection and OnStartupComplete methods. If the COM add-in successfully returns from these two methods, Office removes the sentinel in the registry and everything works fine. If the COM add-in crashes in OnConnection or OnStartupComplete or if you stop debugging and kill the Office process before OnConnection or OnStartupComplete return, the sentinel is still sitting in the registry. When you relaunch the Office application, Office detects that a sentinel got left in the registry on the last run and it disables your COM add-in.It is very easy to have this happen during developmentyou might be stepping through code invoked by your OnConnection or OnStartupComplete entry point and you get to a line of code and say to yourself, "This line of code is completely wrong." You then stop debugging and change the code and press F5 to rerun the COM add-in. But on the second run the COM add-in does not work. Office detects the sentinel in the registry left over from the last run when you killed the process in the middle of OnConnection or OnStartupComplete and it disables your COM add-in.The situation is even worse for unshimmed managed COM add-ins. The sentinel put in the registry for a managed COM add-in is the name of the DLL that bootstraps the COM add-in. In the case of a nonshimmed COM add-in, the bootstrap DLL is always mscoree.dlla component of the CLR. Mscoree.dll acts as a class factory to create COM objects implemented in managed code for a host such as Office that expects a COM object that implements IDTExtensibility2. It bootstraps the CLR into the Office application process, loads the managed COM add-in registered in the registry, and gives the Office application the managed COM add-in class that implements IDTExtensibility2 and through interop makes that class looks like a COM object to Office.So suppose you have two add-in projectsAddin1 and Addin2both of which are unshimmed. You are debugging Addin1's OnConnection handler and you hit Stop Debugging in the middle of it. This leaves the sentinel in the registry saying not that Addin1.dll crashed Office but that mscoree.dll crashed Office. Now you open the Addin2 project and run it, and because Addin2 is also registered with mscoree.dll as its class factory both Addin1 and Addin2 (and any other unshimmed managed add-ins) will be disabled.To un-disable a COM add-in that has been disabled, go to the Help > About box of the Office application and click the Disabled Items button. Doing so pops up a dialog that will let you re-enable mscoree.dll for an unshimmed COM add-in or for a shimmed COM add-in whatever your shim DLL name is.There is a second way your COM add-in can get disabled. If your COM add-in throws an exception in OnConnection or OnStartupComplete code and does not catch it, that exception propagates out to Outlook and Outlook disables the COM add-in by setting the LoadBehavior key to 2 (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\<<Application Name>>\Addins\<<Add-in ProgID>>\LoadBehavior). There is an easy way to deal with this issue. Always put your code that handles OnConnection and OnStartupComplete inside a try..catch block. Do not leak any exceptions in OnConnection or OnStartupComplete back to Office. To un-disable a COM add-in that has been disabled in this way, you can change the LoadBehavior key back to 3 using regedit.exe or re-enable the COM add-in using the COM Add-ins dialog.
A Simple Word COM Add-In
To really understand what is possible with COM add-ins in Office applications, refer to chapters on the object models of Excel (Chapters 35), Word (Chapters 68) and Outlook (Chapters 911). To show that the COM add-in being developed actually works, let's add some code to the OnStartupComplete method of the COM add-in, as shown in Listing 23-4. The code will use the application object to add a button to the standard command bar in Word and show a message box when a user clicks the button.
Listing 23-4. A Simple Word COM Add-In
namespace WordAddin1
{
using System;
using Microsoft.Office.Core;
using Extensibility;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Word = Microsoft.Office.Interop.Word;
[GuidAttribute("581C28BD-E701-4AC1-BD75-0979BCEEC91E"),
ProgId("WordAddin1.Connect")]
public class Connect : Object, Extensibility.IDTExtensibility2
{
private Microsoft.Office.Interop.Word.Application applicationObject;
private Microsoft.Office.Core.COMAddIn addInInstance;
private CommandBarButton simpleButton;
private object missing = System.Reflection.Missing.Value;
public void OnStartupComplete(ref System.Array custom)
{
CommandBars commandBars;
CommandBar standardBar;
commandBars = applicationObject.CommandBars;
// Get the standard CommandBar from Word
standardBar = commandBars["Standard"];
try
{
// try to reuse the button is hasn't already been deleted
simpleButton = (CommandBarButton)standardBar.Controls[
"Word Addin"];
}
catch (System.Exception)
{
// If it's not there add a new button
simpleButton = (CommandBarButton)standardBar.Controls
.Add(1, missing, missing, missing, missing);
simpleButton.Caption = "Word Addin";
simpleButton.Style = MsoButtonStyle.msoButtonCaption;
}
// Make sure the button is visible
simpleButton.Visible = true;
simpleButton.Click += new
CommandBarButtonEvents_ClickEventHandler(
simpleButton_Click);
standardBar = null;
commandBars = null;
}
public void OnBeginShutdown(ref System.Array custom)
{
}
void simpleButton_Click(CommandBarButton ctrl,
ref bool cancelDefault)
{
MessageBox.Show("You clicked on the button");
}
}
}