locked
AddIn doesn't restart when reinstalled - Visual Studio 2010 RRS feed

  • Question

  • I have written 3 AddIns, and as I install and uninstall them, sometimes they show up in Visual Studio, sometimes they don't.

    There are files, xAddIn.Addin and xAddIn.dll in "Documents\Visual Studio 2010\ Addins" folder.

    I select Tools -> Add-in Manager, and the addins are available, checked, and the checkbox below "Start..." is also checked.

    Yet the addin command itself doesn't show up... until I have deleted and re-added the files multiple times, if I start Visual Studio without any project loaded instead of what my preference is, to start a recent solution (from a taskbar pinned shortcut), or (better), after I have restarted Windows.

    This only happens after I remove and add the add-in, or if I update it. Any other time it works perfectly. So I can't see a problem in its Connect.

    Is this a bug related to how Visual Studio updates (or not) its configuration when it is started in a specific form ? It is weird that the AddIn manager shows that my AddIns are loaded, yet they really are not...

    Is this a versioning issue ?

    Is there something I can do to fix this ?

    Note: while I can copy my add-in dll manually, I prefer to use a vsi package I have created, to install it. It actually works better - e.g. in a higher percentage of times - than copying the files by hand - though it should not make any difference; also, deleting old files before installing them works better...

    Wednesday, January 16, 2013 10:38 PM

Answers

  • Hi

    Sorry for answering late, I'm quite busy these days. Hopefully this post solves your problem for good:

    First, some terms needed for the discussion:

    - A "command" is EnvDTE.Command. It is NOT a menu, button or any UI item. It is a mechanism to execute actions. Commands are always "permanent", once an add-in creates them the first time of its life.

    - A "button", "control", "menu", "toolbar", "commandbar", "popup", etc. are UI items. They are NOT commands. They can be permanent or temporary. My recommendation is to use temporary UI items (created when the addin loads, destroyed when the addin unloads).

    Second, to uninstall or reset an add-in to install a new version is overkill and certainly must be avoided. For an add-in that uses a temporary UI, the only reason to reset/uninstall when upgrading is because the new version no longer provides a command (which is extremely unusual). For add-ins with permanent UI the story is more complex.

    Third, if you want a really resilient, upgradable, add-in, avoid the use of ext_ConnectMode.ext_cm_UISetup to create even the (permanent) commands.

    The following is the minimal code of a resilient, upgradable add-in, that:

    1) When loaded (ext_ConnectMode.ext_cm_AfterStartup, ext_ConnectMode.ext_cm_Startup), checks if its command was already created in a previous session. If not, creates it. This provides resilience.

    2) Therefore it avoids the use of ext_ConnectMode.ext_cm_UISetup to create commands.

    3) It creates the (temporary) UI items when loaded (ext_ConnectMode.ext_cm_AfterStartup, ext_ConnectMode.ext_cm_Startup) and removes them when unloaded. Using this temporary UI approach provides resilience too.

    Please create a new add-in with EXACTLY THIS CODE and play with it, upgrading it (changing the text in the Exec method on each new version and just copying the dll), etc. to see that it is really upgradable without resetting or uninstalling the old version.

    Only when this sample works, modify your addins to use the same code.

    I ask this because it is my experience that people tend to use portions of the code of my article http://www.mztools.com/articles/2005/MZ2005003.aspx mixing it with their existing code without fully understanding the "basics" and they end with something plain wrong, like your line of your post above:

    if (connectMode == ext_ConnectMode.ext_cm_Startup || connectMode == ext_ConnectMode.ext_cm_UISetup)

    which misses entirely ext_ConnectMode.ext_cm_AfterStartup.

    Here is the code:

    using System;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.CommandBars;

    namespace MyUpdatableAddin
    {
       public class Connect : Extensibility.IDTExtensibility2, IDTCommandTarget
       {
          // Constants for command properties
          private const string MY_COMMAND_NAME = "MyCommand";
          private const string MY_COMMAND_CAPTION = "My command";
          private const string MY_COMMAND_TOOLTIP = "My command tooltip";

          // Variables for IDE and add-in instances
          private EnvDTE.DTE applicationObject;
          private EnvDTE.AddIn addInInstance;
          private CommandBarButton myStandardCommandBarButton;

          public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode,
             object addInInst, ref System.Array custom)
          {
             try
             {
                applicationObject = (EnvDTE.DTE)application;
                addInInstance = (EnvDTE.AddIn)addInInst;

                switch (connectMode)
                {
                   case ext_ConnectMode.ext_cm_UISetup:

                      // Do nothing for this add-in with temporary user interface
                      break;

                   case ext_ConnectMode.ext_cm_Startup:

                      // The add-in was marked to load on startup
                      // Do nothing at this point because the IDE may not be fully initialized
                      // Visual Studio will call OnStartupComplete when fully initialized
                      break;

                   case ext_ConnectMode.ext_cm_AfterStartup:

                      // The add-in was loaded by hand after startup using the Add-In Manager
                      // Initialize it in the same way that when is loaded on startup
                      AddTemporaryUI();
                      break;
                }
             }
             catch (System.Exception e)
             {
                MessageBox.Show(e.ToString());
             }
          }

          public void OnStartupComplete(ref System.Array custom)
          {
             AddTemporaryUI();
          }

          public void AddTemporaryUI()
          {
             const string VS_STANDARD_COMMANDBAR_NAME = "Standard";

             Command myCommand = null;
             CommandBar standardCommandBar = null;
             CommandBars commandBars = null;
             object[] contextUIGuids = new object[] { };

             try
             {
                // Try to retrieve the command, just in case it was already created, ignoring the
                // exception that would happen if the command was not created yet.
                try
                {
                   myCommand = applicationObject.Commands.Item(addInInstance.ProgID + "." + MY_COMMAND_NAME, -1);
                }
                catch
                {
                }

                // Add the command if it does not exist
                if (myCommand == null)
                {
                   myCommand = applicationObject.Commands.AddNamedCommand(addInInstance,
                      MY_COMMAND_NAME, MY_COMMAND_CAPTION, MY_COMMAND_TOOLTIP, true, 59, ref contextUIGuids,
                      (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
                }

                commandBars = (CommandBars)applicationObject.CommandBars;

                standardCommandBar = commandBars[VS_STANDARD_COMMANDBAR_NAME];

                myStandardCommandBarButton = (CommandBarButton)myCommand.AddControl(standardCommandBar, standardCommandBar.Controls.Count + 1);

                myStandardCommandBarButton.Caption = MY_COMMAND_CAPTION;
                myStandardCommandBarButton.Style = MsoButtonStyle.msoButtonIcon;
                myStandardCommandBarButton.BeginGroup = true;

             }
             catch (System.Exception e)
             {
                MessageBox.Show(e.ToString());
             }
          }

          public void OnDisconnection(Extensibility.ext_DisconnectMode RemoveMode, ref System.Array custom)
          {
             try
             {
                switch (RemoveMode)
                {
                   case ext_DisconnectMode.ext_dm_HostShutdown:
                   case ext_DisconnectMode.ext_dm_UserClosed:

                      if ((myStandardCommandBarButton != null))
                      {
                         myStandardCommandBarButton.Delete(true);
                      }

                      break;
                }
             }
             catch (System.Exception e)
             {
                MessageBox.Show(e.ToString());
             }
          }

          public void OnBeginShutdown(ref System.Array custom)
          {
          }

          public void OnAddInsUpdate(ref System.Array custom)
          {
          }

          public void Exec(string cmdName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
          {

             handled = false;

             if ((executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault))
             {
                if (cmdName == addInInstance.ProgID + "." + MY_COMMAND_NAME)
                {
                   handled = true;
                   MessageBox.Show("Change this text on each new version.");
                }
             }
          }

          public void QueryStatus(string cmdName, vsCommandStatusTextWanted neededText, ref vsCommandStatus statusOption, ref object commandText)
          {
             if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
             {
                if (cmdName == addInInstance.ProgID + "." + MY_COMMAND_NAME)
                {
                   statusOption = (vsCommandStatus)(vsCommandStatus.vsCommandStatusEnabled |
                      vsCommandStatus.vsCommandStatusSupported);
                }
                else
                {
                   statusOption = vsCommandStatus.vsCommandStatusUnsupported;
                }
             }
          }
       }
    }



    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/



    Wednesday, February 6, 2013 3:38 PM

All replies

  • Hi,

    We can use devenv /ResetAddin to reset the addin.

    More information you can refer to:

    http://msdn.microsoft.com/en-us/library/dd998229.aspx

    And we can define when our add-in load via Load Behavior which I think you can refer to:

    http://msdn.microsoft.com/en-us/library/19dax6cz.aspx

    Regards,

    Disley


    崖山之後無中國,明亡之後無華夏

    • Marked as answer by Ego Jiang Thursday, January 31, 2013 2:27 AM
    • Unmarked as answer by Carlos J. Quintero Wednesday, February 6, 2013 3:42 PM
    Friday, January 18, 2013 4:34 AM
  • Hi,

    There are 3 concepts: the add-in is loaded (ie, the assembly is loaded in the AppDomain and the methods of the Connect class are executed), the commands are created and the buttons are created.

    See:

    HOWTO: Adding buttons, commandbars and toolbars to Visual Studio .NET from an add-in.
    http://www.mztools.com/resources_vsnet_addins.aspx

    and

    HOWTO: Troubleshooting Visual Studio and Office add-ins
    (same link)

    I would recommend to post your Connect class because chances are that there is something wrong with the connect flags.


    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/

    • Marked as answer by Ego Jiang Thursday, January 31, 2013 2:27 AM
    • Unmarked as answer by emptyheaded Saturday, February 2, 2013 5:07 AM
    Monday, January 21, 2013 3:41 PM
  • //The essence of my Connect class:

    public class Connect : IDTExtensibility2, IDTCommandTarget { DTE2 _applicationObject; AddIn _addInInstance; CommandBarPopup xProject; public Connect(){} public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { Command objCommand1; CommandBarControl barControl; try { _applicationObject = (DTE2)application; _addInInstance = (AddIn)addInInst; object[] contextGUIDS = new object[] { }; Commands2 commands = (Commands2)_applicationObject.Commands; CommandBars cmdBars = (CommandBars)(_applicationObject.CommandBars); CommandBar vsBar = cmdBars["Project"]; if (connectMode == ext_ConnectMode.ext_cm_UISetup) { objCommand1 = commands.AddNamedCommand2(_addInInstance, ...); } else { objCommand1 = _applicationObject.Commands.Item(_addInInstance.ProgID + "." + m_CommandName1); xProject = (CommandBarPopup)vsBar =(CommandBarPopup)vsBar.Controls.Add(MsoControlType.msoControlPopup, Before: 1); barControl = (CommandBarControl)objCommand1.AddControl(xProject.CommandBar); } } catch (System.ArgumentException /*e*/){} } public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom) { try { if (xProject != null) xProject.Delete(); } catch (System.Exception /*ex*/){} } public void OnAddInsUpdate(ref Array custom){} public void OnStartupComplete(ref Array custom){} public void OnBeginShutdown(ref Array custom){} public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText) { if(neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone) { if (commandName == "myAddIn.Connect.xxx") { status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported|vsCommandStatus.vsCommandStatusEnabled; return; } } public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled) { handled = false; if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault) { if (commandName == "MyAddIn.Connect.xxx") { ... handled = true; return; } } } {

    I created an "uninstaller" - so now I added the /resetAddIn in it, and first test seems to work (after unsuccessfully installing and deleting addins repeatedly).

    Thank you, Disley.

    Still, having to run an "uninstaller" every time I want to update seems... like there is something wrong.

    The AddIn should be able to update itself.

    Or at least, I should be able to put that ResetAddin in the installer ?

    It is hard to make people that will use the addin have to uninstall before update, every time - that is not a typical way to update something...

    I have an installer, .vsi...with a .vscontent similar to below, from http://msdn.microsoft.com/en-us/library/ms246580(v=vs.80).aspx, that I thought should take care of resetting/ overwriting the Addin. Apparently that doesn't happen.

    <VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005"> 
        <Content>
            <FileName>MyAddin.Addin</FileName>
            <FileName>MyAddin.dll</FileName>
            <DisplayName>Example Add-in</DisplayName>
            <Description>An add-in created for this example.</Description>
            <FileContentType>Addin</FileContentType>
            <ContentVersion>1.0</ContentVersion>
        </Content>
    </VSContent>

    Thank you so much for help.
    • Edited by emptyheaded Friday, January 25, 2013 5:49 PM
    Friday, January 25, 2013 4:59 PM
  • Hi,

    Change the OnConnection method to follow this pattern:

    HOWTO: Use correctly the OnConnection method of a Visual Studio add-in

    http://www.mztools.com/articles/2008/MZ2008004.aspx

    Second, put some MessageBoxes in the OnConnection / OnStartupComplete methods, before and after the calls that add/get commands, add buttons, etc. and then test the add-in when a) It is loaded on startup and b) It is loaded by hand in the Add-in Manager. Hopefully you will find a pattern for the failures to show the UI.

    I would recommend to study my sample:

    HOWTO: Adding buttons, commandbars and toolbars to Visual Studio .NET from an add-in.

    http://www.mztools.com/articles/2005/MZ2005003.aspx

    It shows how to use correct patterns, not only for the OnConnection or UI creation, but also for example using constants for Command names and the use of the AddinInstance.ProgId property to avoid hardcoding "MyAddIn.Connect.xxx" in the Exec method and "myAddIn.Connect.xxx" in the QueryStatus method (notice the different case).

    Let us know.


    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/

    • Marked as answer by Ego Jiang Thursday, January 31, 2013 2:27 AM
    • Unmarked as answer by emptyheaded Friday, February 1, 2013 7:20 PM
    Monday, January 28, 2013 8:15 PM
  • What I understand from the links suggested is that Add-ins are "sticky" - and that the only way to remove commands like menu items is from a separate command-line command, separate from the installer.

    And that there is no way to add this type of command to the installer, which makes an installer fairly useless by itself, and copying files by hand in the AddIns directory, simply dangerous.

    I have already created an uninstaller that deletes the Add-in files and resets devenv.

    My question above was, if there was some way to create an upgrade - which means, to include the reset addin in the installer. After all, the installer gives the option to overwrite files - which is misleading, if overwriting files will make the add-in stop functioning altogether. 

    As for my writing "myAddin" in one place and "MyAddin" somewhere else in the above sample - oops, I never press hard enough on the Shift key - so as I copied the namespace which is really the addin name, in the box above, and changed the name, I made a typo. Nobody's addin is named myAddin after all, is it ? 

    I tested the addin with message boxes (uncommented the message boxes shown on catch, plus added more on OnStartupComplete and before/ after commands are created/ deleted, in OnConnection and OnDisconnection.

    Results (that I don't know what to do with):

    - OnConnection: VS seems to always go on the "else " branch. and it gives an exception on the first command:

    objCommand1 = _applicationObject.Commands.Item(_addInInstance.ProgID + "." + m_CommandName1);

    - on the exception message I got "The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG)) - every time I start Visual Studio.
    I also added MessageBox in  

    if (connectMode == ext_ConnectMode.ext_cm_UISetup)

    but that message box never got displayed. 

    - on disconnection... I go through, so if I put a generic message, I get it displayed.  If I put it in the

    if (xProject != null)

    though, I don't get it displayed - so... the commands are not created.

    I really thought that the first time when VS starts, it goes through the ext_ConnectMode.ext_cm_UISetup, then for subsequent uses (like after closing and re-opening solutions) it retains the menu names and updates them... Which obviously isn't true.

    After a bit of experimenting, I have arrived at the following possible solution:

    if (connectMode == ext_ConnectMode.ext_cm_Startup || connectMode == ext_ConnectMode.ext_cm_UISetup)
    {
      try
      {
        objCommand1 = commands.AddNamedCommand2(_addInInstance, m_CommandName1, ...);
      }
      catch (System.ArgumentException e)
      {                        
      //  MessageBox.Show(e.Message, "I know this command already exists");
      }
    }
    // no checking for connectMode, just go
    {
      objCommand1 = _applicationObject.Commands.Item(_addInInstance.ProgID + "." + m_CommandName1);
      ...
    }

    Will this work ? Will it not cause unforeseen consequences ? 

    Either this or something similar to fix the actual add-in creation - or a way to insert the reset addin in the installer, would be the answer to my question...


    Thank you.


    • Edited by emptyheaded Tuesday, February 5, 2013 3:58 PM fixed code
    Friday, February 1, 2013 7:19 PM
  • Hi,

    OK, we will focus on "resetting" the add-in in the installer:

    Assuming that your add-in uses what I call in my articles a "temporary UI" approach (it creates commandbars and buttons when loaded, and removes them when unloaded), the only sticky thing are its commands (and its keyboard bindings). So, resetting an add-in in the installer would mean to delete its commands, and the add-in would have to re-create them when loaded.

    To reset the commands of the addd-in when the new installer runs:

    1) In the installer, execute devenv.exe /resetaddin <addin_name>, so that a) the commands are deleted and b) the add-in will get a ext_ConnectMode.ext_cm_UISetup next time that it is loaded.

    2) In the add-in, use ext_ConnectMode.ext_cm_UISetup to create the commands

    Notice that command keyboard bindings of users of the add-in will be lost each time they run the installer.

    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/

    Saturday, February 2, 2013 5:34 AM
  • i am sorry - i must not have explained properly above, by showing the vscontent sample and the link to the installer package, that the installer i have is a vsi file that packages a few add-ins and project templates.

    i did not see any option in there to add a command line option.

    so i don't understand how to do step 1) above.

    Saturday, February 2, 2013 6:06 AM
  • Hi,

    - I am not familiar with the vscontent / VSI technology to install add-ins. I guess that they don't allow to run "code". Alternatively you can use other installer technologies (MSI-ViX, InnoSetup) that allow to run code / custom actions. You have samples on my web site (http://www.mztools.com/resources_vsnet_addins.aspx), "Articles about installing and uninstalling" section with both technologies.

    - Maybe we should revisit why installing a new version of your add-in requires to uninstall the previous version, or to reset the IDE. As long as it has the same set of (permanent) commands with a temporary UI, it should not be required.


    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/

    Tuesday, February 5, 2013 9:25 AM
  • Thank you for the response.

    The reason I used the vsi is because one of the add-ins requires the use of a project template, and that was what I have found as recommended for installing project templates. I could probably look into other installers - I have used NSIS to install other type of software - 

    The thing is, I just find it "wrong" to have to use a hammer to force an installation, use special tools, when normally a new version of an add-in should replace the old one, no further action required. There must be something wrong in what I am doing, to require such an action.

    I have posted above the errors/ messages I get when I add an add-in, by installing it or copying it, without resetting the previous version (even if there was no "upgrade", the new add-in is identical to what was running before). (Friday, February 01, 2013 7:19 PM)

    The code path the messages are referring to, I have posted on Friday, January 25, 2013 4:59 PM.

    If the add-in is "recognized", the code path goes through the "else" branch (not

    connectMode == ext_ConnectMode.ext_cm_UISetup

     ) and there it finds that it doesn't remember the add-in commands after all, and throws an exception. So the add-in menu items are never really created. As simple as that. A case of Visual Studio Alzheimer's. 

    As for the cure, why is it wrong to do what I suggested ? (with an added bonus of iterating through all the commands in the popup menu item I created, checking if they have been created)

    Or, as an alternative, there is the OnAddInsUpdate method - where I could put all the code to add commands (again, iterating through, because actions do tend to add commands multiple times).

    I fear that the downside may be Visual Studio running slower ?

    I don't have any command keyboard bindings, btw. But if I did... they would be recreated, so it would still be ok (and again, slower... right ?)


    • Edited by emptyheaded Tuesday, February 5, 2013 3:56 PM
    Tuesday, February 5, 2013 3:53 PM
  • Hi

    Sorry for answering late, I'm quite busy these days. Hopefully this post solves your problem for good:

    First, some terms needed for the discussion:

    - A "command" is EnvDTE.Command. It is NOT a menu, button or any UI item. It is a mechanism to execute actions. Commands are always "permanent", once an add-in creates them the first time of its life.

    - A "button", "control", "menu", "toolbar", "commandbar", "popup", etc. are UI items. They are NOT commands. They can be permanent or temporary. My recommendation is to use temporary UI items (created when the addin loads, destroyed when the addin unloads).

    Second, to uninstall or reset an add-in to install a new version is overkill and certainly must be avoided. For an add-in that uses a temporary UI, the only reason to reset/uninstall when upgrading is because the new version no longer provides a command (which is extremely unusual). For add-ins with permanent UI the story is more complex.

    Third, if you want a really resilient, upgradable, add-in, avoid the use of ext_ConnectMode.ext_cm_UISetup to create even the (permanent) commands.

    The following is the minimal code of a resilient, upgradable add-in, that:

    1) When loaded (ext_ConnectMode.ext_cm_AfterStartup, ext_ConnectMode.ext_cm_Startup), checks if its command was already created in a previous session. If not, creates it. This provides resilience.

    2) Therefore it avoids the use of ext_ConnectMode.ext_cm_UISetup to create commands.

    3) It creates the (temporary) UI items when loaded (ext_ConnectMode.ext_cm_AfterStartup, ext_ConnectMode.ext_cm_Startup) and removes them when unloaded. Using this temporary UI approach provides resilience too.

    Please create a new add-in with EXACTLY THIS CODE and play with it, upgrading it (changing the text in the Exec method on each new version and just copying the dll), etc. to see that it is really upgradable without resetting or uninstalling the old version.

    Only when this sample works, modify your addins to use the same code.

    I ask this because it is my experience that people tend to use portions of the code of my article http://www.mztools.com/articles/2005/MZ2005003.aspx mixing it with their existing code without fully understanding the "basics" and they end with something plain wrong, like your line of your post above:

    if (connectMode == ext_ConnectMode.ext_cm_Startup || connectMode == ext_ConnectMode.ext_cm_UISetup)

    which misses entirely ext_ConnectMode.ext_cm_AfterStartup.

    Here is the code:

    using System;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.CommandBars;

    namespace MyUpdatableAddin
    {
       public class Connect : Extensibility.IDTExtensibility2, IDTCommandTarget
       {
          // Constants for command properties
          private const string MY_COMMAND_NAME = "MyCommand";
          private const string MY_COMMAND_CAPTION = "My command";
          private const string MY_COMMAND_TOOLTIP = "My command tooltip";

          // Variables for IDE and add-in instances
          private EnvDTE.DTE applicationObject;
          private EnvDTE.AddIn addInInstance;
          private CommandBarButton myStandardCommandBarButton;

          public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode,
             object addInInst, ref System.Array custom)
          {
             try
             {
                applicationObject = (EnvDTE.DTE)application;
                addInInstance = (EnvDTE.AddIn)addInInst;

                switch (connectMode)
                {
                   case ext_ConnectMode.ext_cm_UISetup:

                      // Do nothing for this add-in with temporary user interface
                      break;

                   case ext_ConnectMode.ext_cm_Startup:

                      // The add-in was marked to load on startup
                      // Do nothing at this point because the IDE may not be fully initialized
                      // Visual Studio will call OnStartupComplete when fully initialized
                      break;

                   case ext_ConnectMode.ext_cm_AfterStartup:

                      // The add-in was loaded by hand after startup using the Add-In Manager
                      // Initialize it in the same way that when is loaded on startup
                      AddTemporaryUI();
                      break;
                }
             }
             catch (System.Exception e)
             {
                MessageBox.Show(e.ToString());
             }
          }

          public void OnStartupComplete(ref System.Array custom)
          {
             AddTemporaryUI();
          }

          public void AddTemporaryUI()
          {
             const string VS_STANDARD_COMMANDBAR_NAME = "Standard";

             Command myCommand = null;
             CommandBar standardCommandBar = null;
             CommandBars commandBars = null;
             object[] contextUIGuids = new object[] { };

             try
             {
                // Try to retrieve the command, just in case it was already created, ignoring the
                // exception that would happen if the command was not created yet.
                try
                {
                   myCommand = applicationObject.Commands.Item(addInInstance.ProgID + "." + MY_COMMAND_NAME, -1);
                }
                catch
                {
                }

                // Add the command if it does not exist
                if (myCommand == null)
                {
                   myCommand = applicationObject.Commands.AddNamedCommand(addInInstance,
                      MY_COMMAND_NAME, MY_COMMAND_CAPTION, MY_COMMAND_TOOLTIP, true, 59, ref contextUIGuids,
                      (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
                }

                commandBars = (CommandBars)applicationObject.CommandBars;

                standardCommandBar = commandBars[VS_STANDARD_COMMANDBAR_NAME];

                myStandardCommandBarButton = (CommandBarButton)myCommand.AddControl(standardCommandBar, standardCommandBar.Controls.Count + 1);

                myStandardCommandBarButton.Caption = MY_COMMAND_CAPTION;
                myStandardCommandBarButton.Style = MsoButtonStyle.msoButtonIcon;
                myStandardCommandBarButton.BeginGroup = true;

             }
             catch (System.Exception e)
             {
                MessageBox.Show(e.ToString());
             }
          }

          public void OnDisconnection(Extensibility.ext_DisconnectMode RemoveMode, ref System.Array custom)
          {
             try
             {
                switch (RemoveMode)
                {
                   case ext_DisconnectMode.ext_dm_HostShutdown:
                   case ext_DisconnectMode.ext_dm_UserClosed:

                      if ((myStandardCommandBarButton != null))
                      {
                         myStandardCommandBarButton.Delete(true);
                      }

                      break;
                }
             }
             catch (System.Exception e)
             {
                MessageBox.Show(e.ToString());
             }
          }

          public void OnBeginShutdown(ref System.Array custom)
          {
          }

          public void OnAddInsUpdate(ref System.Array custom)
          {
          }

          public void Exec(string cmdName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
          {

             handled = false;

             if ((executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault))
             {
                if (cmdName == addInInstance.ProgID + "." + MY_COMMAND_NAME)
                {
                   handled = true;
                   MessageBox.Show("Change this text on each new version.");
                }
             }
          }

          public void QueryStatus(string cmdName, vsCommandStatusTextWanted neededText, ref vsCommandStatus statusOption, ref object commandText)
          {
             if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
             {
                if (cmdName == addInInstance.ProgID + "." + MY_COMMAND_NAME)
                {
                   statusOption = (vsCommandStatus)(vsCommandStatus.vsCommandStatusEnabled |
                      vsCommandStatus.vsCommandStatusSupported);
                }
                else
                {
                   statusOption = vsCommandStatus.vsCommandStatusUnsupported;
                }
             }
          }
       }
    }



    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/



    Wednesday, February 6, 2013 3:38 PM
  • Thank you.

    As a matter of fact, this is the version that I have created last week (and worked), and from behavior point of view, I didn't see the difference to the version I posted.

    My understanding was, that this creates temporary commands... 

    Your explanation makes things very clear. The only thing I don't see is how to make permanent UI - I suppose that would be the situation in which the entire setup was in the ext_ConnectMode.ext_cm_UISetup. And probably there are no such examples on the web, because this is simply unusual and bad.

    Thursday, February 7, 2013 1:28 AM
  • Hi,

    - FWIW, this version does not create "temporary commands", it creates permanent commands (commands, ie, EnvDTE.Command, cannot be temporary) and temporary UI items (menus, buttons, etc.).

    - What it does is that the add-in ensures and enforces that the command exists before creating the temporary UI items.

    - Other add-ins don't enforce that, since they use the ext_ConnectMode.ext_cm_UISetup phase (which is fired only once in the whole life of the add-in, not each session) to create the commands. So, if for some reason the commands are not there in some session, the UI items creation will fail.

    - Finally, the article http://www.mztools.com/articles/2005/MZ2005003.aspx  shows not only how to create UI items but how to create permanent UI items if someone really wants to follow that approach.



    MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/

    Thursday, February 7, 2013 7:07 AM