Sometimes its desirable to integrate added functionality directly into the environment. Add-Ins are a powerful way to do this. In this example well create an add-in that allows you to insert a copyright statement at the top of your code window.
To create an add-in in Visual Studio .NET , open File->New->Project from the menu and choose the Other Projects tab. Pick the Extensibility Projects Folder, and inside this folder choose the Visual Studio .NET Add-in. Change the name to the desired add-in and click OK.
Figure 1 - Choosing an Add-In Project
The next step brings up the Add-In wizard. Click Next when the Welcome wizard screen comes up and this brings up the language choice screen shown below:
Figure 2 Choosing an Add-In Language in the Add-In Wizard
This Wizard screen gives the choice of writing your add-in in either C#, VB, or Visual C++. We choose C# being a mainly a fan of this language. The next wizard screen allows us to run the add-in either in the VS.NET IDE or the Macro IDE or both.
Figure 3 Wizard screen for IDE to insert the add-in
The next screen allows you to add a name and description of your add-in. This will appear later in the Add-In manager dialog located in the Tools menu.
Figure 4 Name and Description Entry screen for the add-in
Clicking Next brings us to a screen that allows us to set certain options for our add-in using various checkboxes. The screen is shown below in Figure 5.
Figure 5 Miscellaneous options for the add-in
The Options screen allows options for the following:
- Create an additional menu item with a happy face in the Tools Menu for activating the Add-in
- Determine when your add-in loads, for example when the VSIDE starts.
- Setup the availability of the add-in, whether all users can use it, or only the person who is currently logged on to the computer.
The next screen gives the programmer the option of adding an about box and some technical support information of who to contact should the add-in produce strange results.
Figure 6 About Box for technical support of the add-in
The last screen is the Summary screen and displays a summary of all of the options the programmer has chosen for our custom add-in. Click Finish to generate a Solution containing an add-in code project and an add-in setup project.
Figure 7 Add-In Wizard Summary Screen
The results of clicking finish are shown below. Only one class is produced in C# called the Connect class. This class is where you can make your initial changes to the addin.
To build the setup project (CopyrRightAddInSetup in this case), youll need to go into the Configuration Manager in the Build menu and check the build, because by default it is unchecked.
Figure 8 CopyRightAddIn Solution and its corresponding Setup Solution
So where do we begin execution of our command? There is a method called Exec created by the wizard. This command is called when the user chooses the Add-In from the Tools menu. This menu item is installed in the Tools menu on start up as we specified in the wizard shown in figure 9 below. You can prove this to yourself right away by running the add-in in debug mode by clicking F5.
Figure 9 Shows Add-In installed in the start menu.
So by clicking the Happy Face in the Tools menu above, the Exec method is called shown in listing 1 below and the executionOption with command name CopyRightAddIn.Connect.CopyrRightAddIn:
Listing 1 - The Exec method in the Connect class of the Add-In
public void Exec(string commandName, EnvDTE.vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if ( executeOption == EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "CopyrRightAddIn.Connect.CopyrRightAddIn")
{
// Add your command execution here
CreateNewFileWithCopyrightInfo();
handled = true;
return;
}
}
}
Now we are ready to right our code that will bring up a code page and add copyright information to it. To execute functionality inside the IDE we need to rely on the IDTExtensibility2 interface. This interface extends the IDE to allow the programmer to take control of its user interface functionality. The add-in wizard creates an object that help us in this endeavor. The declaration for this object is shown below:
private _DTE applicationObject;
The Application object allows us to access all of the hierarchy objects of the extensibility interface. Below is the code that utilizes the application object to create a new code file and inserts the copyright lines into our code
Listing 2 - Using the Add-In to create a New File headed with Copyright information
public void CreateNewFileWithCopyrightInfo()
{
//Create a new text document.
applicationObject.ItemOperations.NewFile("General\\Text File", "",
Constants.vsViewKindCode);
//Get a handle to the new document.
TextDocument objTextDoc = (TextDocument)applicationObject.ActiveDocument.Object("TextDocument");
EditPoint objEditPoint = (EditPoint)objTextDoc.StartPoint.CreateEditPoint();
//Create an EditPoint and add some text.
objEditPoint.Insert(
"// This Code was created by Microgold Software Inc. for educational
purposes" + '\n' +
"// Copyright Microgold Software Inc. " +
DateTime.Now.ToLongDateString());
}
The first line containing ItemOperations is a collection of objects that can perform various file operation tasks in the IDE. The NewFile method of this collection creates a new file based on the specification of parameters passed. The first parameter indicates we are creating a text file. The other two parameters can actually be empty strings, however, we chose the last parameter as a code view to illustrate this enumeration. NewFile creates a TextDocument in the IDE as the current active document. In order to get a handle to this Document, we need to assign an object to the active document. We can use the extDocument to get an EditPoint into the view. With this EditPoint, we can insert new text into the document, replace existing text, delete text, move to the beginning or end of the document, and various other powerful editing activities. In this example, we simply insert text at the beginning of the document. The results of the code are shown below:
Figure 10 Results of running the add-in
A more interesting add-in would be one that opens up every C# file in an existing project and inserts the copyright information at the top. Below is the code that does this. This code steps through each project item in the active solution, opens the file if its a c# file, and sticks the copyright information at the top of the existing file. Then in addition, it saves the file:
Listing 3 - Using the Add-In to create copyright info in every file of a project
public void CreateCopyrightInEveryFileInSolution()
{
try
{
// Create a copyright text for each code item in the project
// Get the array of projects from the active solution
System.Array theProjects = (System.Array)applicationObject.ActiveSolutionProjects;
EnvDTE.Project theProject = null;
// make sure there is an active solution containing projects
if (theProjects.Length > 0)
{
// This add-in only works for the first project in the solution,
// This can be extended for multiple projects by putting a for loop here like
// foreach (Project theProject in theProjects)
theProject = (EnvDTE.Project)(theProjects.GetValue(0));
// Go through each project item in the project
foreach (ProjectItem p in theProject.ProjectItems)
{
Trace.WriteLine("ProjectItem = " + p.Name);
// Check to see if its a c# file
if (p.Name.Substring(p.Name.Length - 3, 3) == ".cs")
{
// Open the file as a source code file
EnvDTE.Window theWindow = p.Open(Constants.vsViewKindCode);
//Get a handle to the new document in the open window
TextDocument objTextDoc = (TextDocument)theWindow.Document.Object("TextDocument");
EditPoint objEditPoint = (EditPoint)objTextDoc.StartPoint.CreateEditPoint();
//Create an EditPoint and add some text.objEditPoint.Insert("// This Code was created by
Microgold Software Inc. for educational purposes" + '\n' +
"// Copyright Microgold Software Inc. " + DateTime.Now.ToLongDateString() + "\n\n");
// Get the filename from the project item and save the file with the copyright information
string fName = p.get_FileNames(0);
p.SaveAs(fName);
}
// end if ".cs" file
}
// end for each project item
}
// end if any projects exists in projects collection
}
// end try block
catch (Exception e)
{
Trace.WriteLine(e.Message.ToString());
}
}
If you setup your add-in to run on startup of the IDE, you can execute code any initialization code for your add-in at startup in the OnConnection event handler shown below:
Listing 4 - The OnConnection Event Handler in the Connect Object
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
applicationObject = (_DTE)application;
addInInstance = (AddIn)addInInst;
if(connectMode == Extensibility.ext_ConnectMode.ext_cm_UISetup)
{
object []contextGUIDS = new object[] { };
Commands commands = applicationObject.Commands;
_CommandBars commandBars = applicationObject.CommandBars;
// When run, the Add-in wizard prepared the registry for the
// Add-in.
// At a later time, the Add-in or its commands may become
// unavailable for reasons such as:
// 1) You moved this project to a computer other than which is was
// originally created on.
// 2) You chose 'Yes' when presented with a message asking if you wish // to remove the
dd-in.
// 3) You add new commands or modify commands already defined.
// You will need to re-register the Add-in by building the CopyrRightAddInSetup project,
// right-clicking the project in the Solution Explorer, and then choosing // install.
// Alternatively, you could execute the ReCreateCommands.reg file the
// Add-in Wizard generated in
// the project directory, or run 'devenv /setup' from a command prompt.
try
{
Command command = commands.AddNamedCommand(addInInstance,
"CopyrRightAddIn", "CopyrRightAddIn",
"Executes the command for CopyrRightAddIn",
true, 59, ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported
+(int)vsCommandStatus.vsCommandStatusEnabled);
CommandBar commandBar = (CommandBar)commandBars["Tools"];
CommandBarControl commandBarControl =
command.AddControl(commandBar, 1);
}
catch(System.Exception e /*e*/)
{
Trace.WriteLine(e.Message.ToString());
}
}
}
In the default routine generated by the add-in wizard, the menu item is inserted into the Tools menu if the Add-In is running for the very first time. The enumeration ConnectMode is used to determine if this is the first time the add-in is run. If the connectMode is set to ext_cm_UISetup, the try block is executed. Below are a few more connect mode enumerations:
ConnectMode |
Description |
ext_cm_AfterStartup |
The Add-In was loaded after the IDE was started, probably from the Add-In Manager |
ext_cm_Startup |
Add-In was loaded when the the IDE was started |
ext_cm_External |
Add-in was loaded by another program other than the IDE |
ext_cm_CommandLine |
the devenv command loaded the add-in. devenv is a tool that lets you set various options for the IDE from the command line |
ext_cm_Solution |
Add-In loaded when a Project was loaded with a dependency on the add-in |
ext_cm_UISetup |
This connection mode is set only once, when the add-in is loaded for the very first time |
Below is a UML Sequence diagram that tries to illustrate what is happening with the AddIn
Figure 11 - Sequence Diagram illustrating Add-In behavior drawn using WithClass 2000
Basically, if the user Launches the host and the add-in set up to run on start-up, OnConnect is called. Also OnConnect is called if the user chooses to enable the add-in in the Add-In Manager. When the Add-In function is chosen from the Tools menu, Exec is called which in turn calls the CreateNewFileWithCopyrightInfo (or CreateCopyrightInEveryFileInSolution as in the second example).
Conclusion
Add-Ins are fairly easy to create using the Add-In Wizard. Debugging is another story. I found that you had to be careful when building the add-in because if the add-in was launched on startup, you cant rebuild if you make changes because the add-in is running. What you have to do is turn off the add-in in the add-in manager, leave IDE and then relaunch the IDE. This problem seemed to disappear when I made sure not to exit the IDE of the host application that was launched to debug the add-in and exit by stopping the debugger. The extensibility interface is fairly powerful and you can literally create myriads of add-ins that will enhance your programming experience and increase your efficiency. Hopefully this article is enough to start you on your way!