Removing a Command
Once created, if a command is no longer needed (for example, if your add-in is being uninstalled), you can delete it. Only commands created by add-ins can be deleted, however; commands defined by Visual Studio .NET or commands for macros can't be removed and an error is generated if a call is made to remove a non-add-in created command. To remove a command, you use the Command.Delete method; it removes not only the command name but also any user interface elements for that command, such as buttons on command bars.
Sub DeleteMyCommand()
DTE.Commands.Item("MyAddin.Connect.MyCommand").Delete()
End Sub
Uninstalling an add-in should remove any commands that it has created, but by default, the code generated by the Add-in Wizard doesn't do this. You can add a custom action to your add-in component to remove these commands. A custom action is code that is run by the installer project created by the Add-in Wizard during install and/or uninstall time. During install and uninstall, the installer loads your add-in component and searches for a public class that derives from the System.Configuration.Install.Installer class and uses the RunInstaller(true) attribute. If a class meets these criteria, the methods Install and Uninstall are called, allowing any custom code to be run. Creating a component of this type is simple. In the Add New Item dialog box, you can insert an Installer Class project item, as shown in Figure 7-3.
Figure 7-3. The Add New Item dialog box, with the Installer Class item selected

The following code implements the Uninstall and Install methods of the Installer class. These methods first check to make sure that no instances of the devenv.exe (the executable for Visual Studio .NET) process are running. These methods prompt the user to close any instances if any are running, and when they have been closed, the custom action starts a new instance of Visual Studio .NET through the automation model. The Uninstall method then continues on, calling the Delete method to delete the command. You can modify this code to find and delete the commands that your add-in has created.This code also helps install an add-in using the Install method. When the CommandPreload flag is set in the registry, the next time Visual Studio .NET runs it starts the process of allowing add-ins to create commands. You've seen the problems that can arise if Visual Studio .NET is closed unexpectedly or if multiple instances are created before the command information can be written to disk. Using a custom action, you can create an instance of Visual Studio .NET through the automation model and then immediately close the process. This allows Visual Studio .NET to create any commands and save this data, allowing your user to avoid the trap of command information being overwritten.
bool IsVSRunning()
{
System.Diagnostics.Process []processes =
System.Diagnostics.Process.GetProcesses();
foreach(System.Diagnostics.Process process in processes)
{
//Wrap in a try catch. If the process is not owned by
//the user then an exception will be thrown trying to
//get the ProcessModule
try
{
if((process != null) && (process.MainModule != null))
{
//Get the file name of the process,
// and compare it to 'devenv.exe'
string fileName = process.MainModule.FileName;
fileName = System.IO.Path.GetFileName(fileName);
if(System.String.Compare(fileName, "devenv.exe") == 0)
return true;
}
}
catch(System.Exception ex)
{
}
}
return false;
}
public override void Uninstall(System.Collections.IDictionary savedState)
{
base.Uninstall(savedState);
System.Windows.Forms.MessageBox.Show("Uninstall");
while(IsVSRunning())
{
System.Windows.Forms.MessageBox.Show("A running instance of Visual " +
Studio .NET was found.\n\nPlease close all copies of Visual " +
"Studio .NET, then press OK.", "Uninstall...",
System.Windows.Forms.MessageBoxButtons.OK);
}
EnvDTE.DTE dte = new EnvDTE.DTEClass();
//Change the name of the command below
//to the name of the command you have created
// This can be called multiple times, once for each command created
dte.Commands.Item("MyAddin1.Connect.MyAddin1", -1).Delete();
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
System.Windows.Forms.MessageBox.Show("Install");
while(IsVSRunning())
{
System.Windows.Forms.MessageBox.Show("A running instance of Visual " +
"Studio .NET was found.\n\nPlease close all copies of Visual " +
"Studio .NET, then press OK.", "Install...",
System.Windows.Forms.MessageBoxButtons.OK);
}
EnvDTE.DTE dte = new EnvDTE.DTEClass();
}
Once this component has been added to your add-in's project, the last step is to add it to the list of custom actions available to the installer. You do this by opening the Custom Actions editor of the installer project: right-click the setup project and choose View | Custom Actions. Next, you add the primary output of the add-in project to the Uninstall and Install nodes of this editor by right-clicking on each of these nodes in the Custom Actions window, choosing Add Custom Action, double-clicking on the Application Folder item in the Select Item In Project dialog box, and selecting Primary Output From Addin Project Name.You don't need to add a custom action to your installer project to delete your commands during uninstall. Deleting commands that are no longer used results in the best user experience, but you don't need to manually remove the commands you create. When your add-in is uninstalled, it leaves behind any commands and the user interface for those commands. If the user chooses any of the commands for which the add-in that handles that command can't be found, the user is presented with the message box shown earlier in Figure 7-2. If the user clicks Yes, the command and any of its user interface elements are removed.