Logo
PRB: Unable to add buttons to toolbars of toolwindows of Visual Studio from an add-in

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2005
Date: April 2010   Microsoft Visual Studio 2008
Updated: March 2013   Microsoft Visual Studio 2010
      Microsoft Visual Studio 2012
Introduction

This article discusses a problem that happens when an add-in tries to add a button to the toolbar of a toolwindow of Visual Studio, such as to the Solution Explorer toolbar (not to confuse with its context menu).

More Information

Some Visual Studio toolwindows have a toolbar (apart from a context menu for their contents). Until Visual Studio 2005 it was not possible for an add-in to get access to the toolbar of a toolwindow to add buttons to it, but the new EnvDTE80.dll assembly introduced in Visual Studio 2005 provided a new EnvDTE80.Window2 class (that represents a toolwindow) with a new CommandBars property that didn't exist in the EnvDTE.Window class. Since with this property it is possible to get the CommandBar that represents the toolbar, in theory it would be possible to add CommandBarControls (CommandBarButtons or CommandBarPopups) to it, as described in the article HOWTO: Adding buttons, commandbars and toolbars to Visual Studio .NET from an add-in. In practice it is not possible due to these exceptions that happen on each Visual Studio version when adding or removing the button:

  • Visual Studio 2005: the button is added correctly, but you get "System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT_FAIL has been returned from a call to a COM Component." when removing the control calling CommandBarControl.Delete(). Trying to removing the button deleting its associated command (the technique described in the article HOWTO: Prevent dead CommandBarButtons when Visual Studio or an add-in crashes) doesn't work either.
  • Visual Studio 2008: "System.UnauthorizedAccessException: Access denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))" when calling Command.AddControl(...) to add the button.
  • Visual Studio 2010 and higher: "System.UnauthorizedAccessException: Controls cannot be added to a Toolwindow toolbar." when calling Command.AddControl(...) to add the button.

So, while the Visual Studio 2005 version could have a bug, it is clear in next versions that add-ins are not allowed to add buttons to the toolbars of toolwindows.

The following add-in provides the code to reproduce the problem:

Language: VB.NET   Copy Code Copy Code (IE only)
Imports System
Imports Microsoft.VisualStudio.CommandBars
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80

Public Class Connect
    Implements IDTExtensibility2
    Implements IDTCommandTarget

    Private Const _COMMAND_NAME As String = "MyCommand"

    Private _DTE2 As DTE2
    Private _addIn As AddIn
    Private _commandBarControl As CommandBarControl
    Private _command As Command = Nothing

    Public Sub OnConnection(ByVal application As Object, ByVal connectMode As ext_ConnectMode, _
        ByVal addInInst As Object, ByRef custom As Array) Implements IDTExtensibility2.OnConnection

        Dim window2 As Window2
        Dim commandBars As CommandBars
        Dim commandBar As CommandBar

        Try

            _DTE2 = CType(application, DTE2)
            _addIn = CType(addInInst, AddIn)

            Select Case connectMode

                Case ext_ConnectMode.ext_cm_AfterStartup, ext_ConnectMode.ext_cm_Startup

                    Try
                        _command = _DTE2.Commands.Item(_addIn.ProgID & "." & _COMMAND_NAME)
                    Catch
                    End Try

                    If _command Is Nothing Then
                        _command = _DTE2.Commands.AddNamedCommand(_addIn, _COMMAND_NAME, "My command", _
                           "My command", True, 59)
                    End If

                    window2 = DirectCast(_DTE2.Windows.Item(EnvDTE.Constants.vsWindowKindSolutionExplorer), Window2)

                    commandBars = DirectCast(window2.CommandBars, CommandBars)
                    commandBar = commandBars.Item("Explorer")

                    _commandBarControl = DirectCast(_command.AddControl(commandBar), CommandBarControl)
                    _commandBarControl.Visible = True

            End Select

        Catch ex As Exception

            System.Windows.Forms.MessageBox.Show(ex.ToString)

        End Try

    End Sub

    Public Sub OnDisconnection(ByVal disconnectMode As ext_DisconnectMode, ByRef custom As Array) _
        Implements IDTExtensibility2.OnDisconnection

        Try

            If Not (_commandBarControl Is Nothing) Then
                _commandBarControl.Delete()
            End If

            If Not (_command Is Nothing) Then
                _command.Delete()
            End If

        Catch ex As Exception

            System.Windows.Forms.MessageBox.Show(ex.ToString)

        End Try

    End Sub

    Public Sub OnAddInsUpdate(ByRef custom As Array) Implements IDTExtensibility2.OnAddInsUpdate
    End Sub

    Public Sub OnStartupComplete(ByRef custom As Array) Implements IDTExtensibility2.OnStartupComplete
    End Sub

    Public Sub OnBeginShutdown(ByRef custom As Array) Implements IDTExtensibility2.OnBeginShutdown
    End Sub

    Private Sub QueryStatus(ByVal CmdName As String, ByVal NeededText As vsCommandStatusTextWanted, _
        ByRef StatusOption As vsCommandStatus, ByRef CommandText As Object) Implements IDTCommandTarget.QueryStatus

        If NeededText = vsCommandStatusTextWanted.vsCommandStatusTextWantedNone Then

            If CmdName = _addIn.ProgID & "." & _COMMAND_NAME Then

                StatusOption = vsCommandStatus.vsCommandStatusSupported Or vsCommandStatus.vsCommandStatusEnabled

            End If

        End If

    End Sub

    Private Sub Exec(ByVal CmdName As String, ByVal ExecuteOption As vsCommandExecOption, ByRef VariantIn As Object, _
        ByRef VariantOut As Object, ByRef Handled As Boolean) Implements IDTCommandTarget.Exec

        If CmdName = _addIn.ProgID & "." & _COMMAND_NAME Then

            System.Windows.Forms.MessageBox.Show(CmdName & " clicked.")

        End If

    End Sub

End Class
Language: C#   Copy Code Copy Code (IE only)
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;

namespace MyAddin
{
    public class Connect : IDTExtensibility2, IDTCommandTarget
    {
        const string _COMMAND_NAME = "MyCommand";

        private DTE2 _DTE2;
        private AddIn _addIn;
        private CommandBarControl _commandBarControl;
        private Command _command;

        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            Window2 window2;
            CommandBars commandBars;
            CommandBar commandBar;
            object[] contextUIGuids = new object[] { };

            try
            {
                _DTE2 = (DTE2)application;
                _addIn = (AddIn)addInInst;

                switch (connectMode)
                {
                    case ext_ConnectMode.ext_cm_AfterStartup:
                    case ext_ConnectMode.ext_cm_Startup:
                    {
                        try
                        {
                           _command = _DTE2.Commands.Item(_addIn.ProgID + "." + _COMMAND_NAME, -1);
                        }
                        catch
                        {
                        }

                        if (_command == null)
                        {
                            _command = _DTE2.Commands.AddNamedCommand(_addIn, _COMMAND_NAME, "My command", 
                               "My command", true, 59, ref contextUIGuids, 
                               (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
                        }

                        window2 = (Window2)_DTE2.Windows.Item(EnvDTE.Constants.vsWindowKindSolutionExplorer);

                        commandBars = (CommandBars)window2.CommandBars;
                        commandBar = commandBars["Explorer"];

                        _commandBarControl = (CommandBarControl)_command.AddControl(commandBar, 1);
                        _commandBarControl.Visible = true;
                    }
                    break;
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString());
            }
        }

        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
            try
            {
                if (_commandBarControl != null)
                {
                    _commandBarControl.Delete(true);
                }

                if (_command != null)
                {
                    _command.Delete();
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString());
            }
        }

        public void OnAddInsUpdate(ref Array custom)
        {
        }

        public void OnStartupComplete(ref Array custom)
        {
        }

        public void OnBeginShutdown(ref Array custom)
        {
        }

        public void QueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, 
            ref vsCommandStatus StatusOption, ref object CommandText)
        {
            if (NeededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
            {
                if (CmdName == _addIn.ProgID + "." + _COMMAND_NAME)
                {
                    StatusOption = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
                }
            }
        }

        public void Exec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn,
            ref object VariantOut, ref bool Handled)
        {
            if (CmdName == _addIn.ProgID + "." + _COMMAND_NAME)
            {
                System.Windows.Forms.MessageBox.Show(CmdName + " clicked.");
            }
        }
    }
}

Go back to the 'Resources for Visual Studio .NET extensibility' section for more articles like this


Top