A few years ago I gave a presentation to our local .NET User Group on creating Add-Ins for Visual Studio .NET using the .NET framework. That presentation was a lot of fun and very well received. Creating Add-Ins isn't rocket science by any stretch but it's not without its couple gotchas.
Visual Studio .NET provides a project wizard that accelerates the creation of Add-Ins but it by no means necessary. If you use the wizard to create your project, it will yield two projects: your Add-In project and a deployment project. The deployment project is not necessary either, but provides a convenient mechanism from within the IDE to install and uninstall your Add-In. When creating your Add-In using the wizard you'll want to be careful to name your Add-In appropriately; should you decide to rename your Add-In later you'll need to make several changes.
The most basic of Add-Ins consists simply of a class that implements the IDTExtensibility2 interface. This interface, available via a reference to Extensibility.dll, provides a set of methods useful for interacting with the Add-In's host environment. The wizard will create a class called Connect for you that already has the method stubs for each of the interface's methods. Being a masochist when it comes to code, I prefer to write it out by hand, but the wizard surely gives you a head start when creating your Add-Ins.
Despite being created using the .NET framework, IDTExtensibility2 Add-Ins need to be exposed to their host environments as COM objects (which makes sense being that the IDTExtensibility2 interface is a COM interface). Therefore, it is necessary to take some action to facilitate this interoperation.
1. Apply the GuidAttribute to your assembly; this effectively becomes the TypeLibID for your assembly.
2. Assign a ProgIdAttribute to your class to define a unique, friendly name through which COM-based clients can instantiate your Add-In (e.g. CoCreateInstance, CreateObject, etc)
3. Assign a GuidAttribute to your class to establish the CLSID.
4. Apply the ClassInterfaceAttribute to your class with a value of ClassInterfaceType.AutoDispatch; this is not technically required, but I prefer to do this.
5. Create a strong name (via sn.exe) and apply it to the assembly via the AssemblyKeyFileAttribute.
6. Assign a version to the project via the AssemblyVersionAttribute.
If you are building your Add-In from within Visual Studio, open the project properties and make sure the 'Register for COM Interop' is set to true. If, on the otherhand, you prefer command-line builds as I frequently do, you'll need to register it manually with the Assembly Registration Utility (RegAsm.exe).
For deeper integration with the host, you'll probably need to know more about the environment in which the Add-In will run. The host environment will dictate in many ways the references you set in your project.
The IDTExtensibility2.OnConnection method, for example, accepts among other two parameters named application and addInInst. For an Add-In that targets the VS.NET environment, the application object is really an EnvDTE._DTE object and the addInInst is an EnvDTE.AddIn object as illustrated in the following example:
using EnvDTE;
using Extensibility;
private _DTE _app;
private AddIn _addIn;
void IDTExtensibility2.OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) {
_app = (_DTE)application;
_addIn = (AddIn)addInInst;
}
However, if you're dealing with an Office Add-In, you have some other things to consider. First is the question of references. You may be tempted to simply add a reference to the appropriate COM library (e.g. Microsoft Excel 11.0 Object Library), but unless you installed Office properly, that is probably not the right course of action.
When developing Add-Ins for Office, you want to set a reference to the appropriate Primary Interop Assembly (PIA). If the PIA is not registered in the GAC or inaccessible, VS.NET will, upon selecting the COM object library, create an interop assembly (e.g. Interop.Excel.dll) in your project folder. This is not really a good thing; among other things it's not signed so if you signed your assembly it won't work.
You might be tempted to create your own using the Type Library Importer tool (TlbImp.exe). In fact, you could do something similar to the following to create your own, signed interop assembly (in this case, for Outlook):
tlbimp msoutl.olb /out:Devstone.Interop.Outlook.dll
/namespace:Devstone.Interop.Outlook /asmversion:1.0.0.0 /keyfile:c:\junk\mytestkey.snk
In fact, this would work, but it's not the recommended way to approach the problem.
The recommendation is to establish a reference to the vendor-supplied PIA. Only the publisher of a type library can produce a true PIA. Fortunately, Microsoft has created PIAs for each of the Office applications, but they are not installed by default in a Typical or Custom installation of Office. You must manually select them.
Once installed, you can set your reference to the COM type library using the standard mechanisms. Visual Studio .NET will find the PIA registered in the GAC and will set a reference to the PIA rather than dynamically generating the interop assembly. VS.NET will also set a reference to Microsoft.Office.Core.dll (which is the Microsoft Office 11.0 Object Library in the case of Office 2003).
The code from above may resemble the following for an Outlook Add-In:
using Microsoft.Office.Core;
using outlook = Microsoft.Office.Interop.Outlook;
using Extensibility;
private outlook.Application _app;
private COMAddIn _addIn;
void IDTExtensibility2.OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) {
_app = (outlook.Application)application;
_addIn = (COMAddIn)addInInst;
}
The next step is the matter of registering the Add-In with the target Office application. This is done in the registry. If you've created the Add-In project using the wizard and have the deployment project, it contains these settings for you. Otherwise, you may have to create them manually. For development and testing purposes, this key must exist. So this is what I do:
1. Create the AddIns key in HKCU\Software\Microsoft\Office\Outlook
2. Create a new key in the Addins key with the same value as the ProgIdAttribute assigned to the Add-In class
3. In the ProgId key, create a DWORD value named LoadBehavior and give it a value of 3
4. Create a string value named FriendlyName and give it the name as you would like it to appear within Office
5. Create a string value named Description and assign it a value accordingly
6. Export the ProgId key to a .reg file for later use
7. Add the .reg file to my VS.NET Add-In project for quick access and so it automatically incorporates itself into source control
After that, you're pretty much off the races. Creating Add-Ins can be a lot of fun and also pretty rewarding.
Happy coding!