Visual Studio Developer Center >
Visual Studio Forums
>
Visual Studio Extensibility
>
ext_cm_AfterStartup vs ext_cm_Startup
ext_cm_AfterStartup vs ext_cm_Startup
- Hi
After much stress, I read this helpful article by Carlos.
I therefore modified my code (which had erroneously been using ext_cm_UISetup) to do its OnConnection work when it see ext_cm_Startup.
However, this does not work, I add a menu item to the 'Projects' menu in Visual Studio, but it does not appear when I install and test the add-in on an XP box.
Under debug on Vista it works, the menu item appears as does all the functionality.
However...
If I change the code to do its OnConnection when it sees ext_cm_AfterStartup, then install the add-in on the XP box, I can get it to work...
I do this by starting Visual Studio 2008, disabling/unloading the add-in, closing VS 2008.
Start VS2008 again, load some arbitrary test project, re-enable the add-in (by going to Tools menu and accessing the add-ins list).
This causes the add-in to run its OnConnection, but this then works, I load some project, right-click it and hey-presto the new menu item is there and all functionality.
I've inserted various msg boxes etc as a testing aid, and the code does not seem to fail or get errors.
I know that it runs when it is auto-loaded and I do see ext_cm_Startup being passed too.
So in brief, my add-in only works on the test XP box when I manually load (and use ext_cm_AfterStartup) but it does not work when I autoload it (and use ext_cm_Startup).
In both cases the OnConnection code is the same, it runs fine, I print various steps etc and I see no errors.
It is simply that for some reason I cannot get the menu item to appear when the add-in is autoloaded, no matter how I code it.
Could this be due to XP? might it be timing related?
The Vista box is x64 and very fast, the text XP box is 32-bit notebook and far less powerful.
Any ideas much appreciated.
Cap'n
Answers
- Hello,
1) See:
HOWTO: Use correctly the OnConnection method of a Visual Studio add-in
Any discussion about why something doesn't work is pointless if you have code like this:
if (connectMode == ext_ConnectMode.ext_cm_Startup) //|| (connectMode == ext_ConnectMode.ext_cm_AfterStartup))
So please do stick to this pattern and never, ever, change it:
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)
{
System.Windows.Forms.MessageBox.Show(e.ToString());
}
}
public void OnStartupComplete(ref System.Array custom)
{
AddTemporaryUI();
}
2) Commandbar names are not unique, and the "Project" commandbar name can be quite "reused" by different project types. So, CommandBars["Project"] can return a wrong commandbar and you won't see your button. FWIW Commandbars are uniquely identified by a GUID and ID. So, until you master the OnConnection method and it works on all machines, use the "Solution" commandbar name which is unique and doesn't have duplicates. Once you check that your button appears on the Solution context menu on Windows Vista and XP, on debugging and not debugging, loading the add-in on startup or manually through the Add-In Manager, then we will focus on the Project context menu.
MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about VS extensibility: http://msmvps.com/blogs/carlosq/- Proposed As Answer byNathan Halstead [MSFT]ModeratorThursday, November 05, 2009 2:18 AM
- Marked As Answer byChao KuoMSFT, ModeratorThursday, November 05, 2009 3:40 AM
- Hello,
I'm glad that the original problem was solved.
About the problem of getting the "Project" menu, the usual way would be:
CommandBar projectCommandBar = DTE.CommandBars["Project"]
(using bar2.Controls["Project"] is bound to cause problems since controls don't have a name but a caption, and it is localized. Commandbars have names and are not localized)
I know for sure that there is at least one commandbar named "Project", which is used for Windows Forms projects. The problem can arise if Visual Studio has more than one commandbar named "Project", since in that case which one you get is not predictable. If you get the wrong one, you won't see your button because it is on another commandbar. Such additional "Project" commandbars can be contributed by some project types that you install on VS such as SilverLight, SharePoint, XNA and others. See:
INFO: Testing a Visual Studio add-in against new Visual Studio tools or extensions from Microsoft
To verify if this is the problem:
- Test the add-in on a VS where it works, so you know that your code is correct and the button is there.
- Then, on the system where the button is not there, run this code so you verify that the problem is caused by a second commandbar with the "Project" name:
foreach (CommandBar commandBar in DTE.CommandBars)
{
if (commandBar.Name = "Project")
{
MessageBox.Show("I am commandbar with name 'Project'");
}
}
If you get more than one MessageBox, then that is the problem. In that case you need to use a service (IVsProfferCommands.FindCommandBar) from the SDK, which sounds scary but it can be done from an add-in, so start familiarizing with the approach:
HOWTO: Get a Visual Studio service from an add-in
http://www.mztools.com/articles/2007/MZ2007015.aspx
and the details of the solution are explained here:
Using IVsProfferCommands to retrieve a Visual Studio CommandBar
http://blogs.msdn.com/dr._ex/archive/2007/04/17/using-ivsproffercommands-to-retrieve-a-visual-studio-commandbar.aspx
and here:
Finding GUID / menuid for Project in Solution Explorers
http://social.msdn.microsoft.com/Forums/en/vsx/thread/4e1ee28f-d3f5-4702-b9ac-63c08d00a989
MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about VS extensibility: http://msmvps.com/blogs/carlosq/- Proposed As Answer byNathan Halstead [MSFT]ModeratorWednesday, November 04, 2009 2:09 AM
- Marked As Answer byChao KuoMSFT, ModeratorThursday, November 05, 2009 3:40 AM
All Replies
- OK I have more info on this odd behavior.
I created a new add-in project in VS 2008, I then modified the OnConnection method to check for ext_cm_Startup and then add a menu item to the top of the project context menu (i.e. the menu you see when you right-click on a project in the solution explorer).
I then installed the add-in on an XP box and a Vista box (by placing the DLL and .Addin file into "Documents\Visual Studio 2008\Addins")
I then simply start Visual Studio 2008, load some test project and right click the project.
On Vista, the menu item appears in Visual Studio 2008, but on XP it does not.
Here is the test code:
using System; using Extensibility; using EnvDTE; using EnvDTE80; using Microsoft.VisualStudio.CommandBars; using System.Diagnostics; using System.Windows.Forms; namespace AddinProblemInvestigation { /// <summary>The object for implementing an Add-in.</summary> /// <seealso class='IDTExtensibility2' /> public class Connect : IDTExtensibility2 { /// <summary>Implements the constructor for the Add-in object. Place your initialization code within this method.</summary> public Connect() { } /// <summary>Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.</summary> /// <param term='application'>Root object of the host application.</param> /// <param term='connectMode'>Describes how the Add-in is being loaded.</param> /// <param term='addInInst'>Object representing this Add-in.</param> /// <seealso class='IDTExtensibility2' /> public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)application; _addInInstance = (AddIn)addInInst; if (connectMode == ext_ConnectMode.ext_cm_Startup) //|| (connectMode == ext_ConnectMode.ext_cm_AfterStartup)) { MessageBox.Show("Menu setup code has been entered.","Diagnostic Info",MessageBoxButtons.OK); CommandBar bar = ((CommandBars)(_applicationObject.CommandBars))["Project"]; CommandBarPopup oPopup = (CommandBarPopup)bar.Controls.Add(MsoControlType.msoControlPopup, System.Reflection.Missing.Value, System.Reflection.Missing.Value, 1, true); oPopup.Visible = true; oPopup.Enabled = true; oPopup.Caption = "Test Menu Item"; } } /// <summary>Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.</summary> /// <param term='disconnectMode'>Describes how the Add-in is being unloaded.</param> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom) { } /// <summary>Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.</summary> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnAddInsUpdate(ref Array custom) { } /// <summary>Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.</summary> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnStartupComplete(ref Array custom) { } /// <summary>Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.</summary> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnBeginShutdown(ref Array custom) { } private DTE2 _applicationObject; private AddIn _addInInstance; } }
Even if I do the menu setup code in OnStartupComplete it still fails on XP, so there is a difference when running this add-in on XP, I have no idea what I am doing wrong.
Thanks
Cap'n- Edited byCaptain Kernel Sunday, November 01, 2009 2:30 PMclarify
- I may have fixed this, but I really don't know the best way to code this anymore.
I changed the way I access the "Project" context popup menu. Rather than the earlier method, I now do it in two stages, getting the parent "Project and Solution Context Menus" first and then within that, getting the "Project" menu, amazingly this works on XP now.
This was just an experiment that I didn't think would yield anything, but it has, does anyone know the correct way to code this stuff??
public void OnStartupComplete(ref Array custom) { CommandBars commandBars; commandBars = (CommandBars)_applicationObject.CommandBars; GetCommandBarNameByControlCaption(_applicationObject,"Project"); MessageBox.Show("OnStartupComplete has begun.", "Diagnostic Info", MessageBoxButtons.OK); //CommandBar bar = ((CommandBars)(_applicationObject.CommandBars))["Project"]; CommandBar bar2 = commandBars["Project and Solution Context Menus"]; CommandBarPopup con2 = (CommandBarPopup)bar2.Controls["Project"]; CommandBarPopup oPopup = (CommandBarPopup)con2.Controls.Add(MsoControlType.msoControlPopup, System.Reflection.Missing.Value, System.Reflection.Missing.Value, 1, true); /* CommandBarPopup oPopup = (CommandBarPopup)bar.Controls.Add(MsoControlType.msoControlPopup, System.Reflection.Missing.Value, System.Reflection.Missing.Value, 1, true); */ oPopup.Visible = true; oPopup.Enabled = true; oPopup.Caption = "Test Menu Item"; } <br/><br/><br/><br/><br/><br/><br/><br/>
- Hello,
1) See:
HOWTO: Use correctly the OnConnection method of a Visual Studio add-in
Any discussion about why something doesn't work is pointless if you have code like this:
if (connectMode == ext_ConnectMode.ext_cm_Startup) //|| (connectMode == ext_ConnectMode.ext_cm_AfterStartup))
So please do stick to this pattern and never, ever, change it:
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)
{
System.Windows.Forms.MessageBox.Show(e.ToString());
}
}
public void OnStartupComplete(ref System.Array custom)
{
AddTemporaryUI();
}
2) Commandbar names are not unique, and the "Project" commandbar name can be quite "reused" by different project types. So, CommandBars["Project"] can return a wrong commandbar and you won't see your button. FWIW Commandbars are uniquely identified by a GUID and ID. So, until you master the OnConnection method and it works on all machines, use the "Solution" commandbar name which is unique and doesn't have duplicates. Once you check that your button appears on the Solution context menu on Windows Vista and XP, on debugging and not debugging, loading the add-in on startup or manually through the Add-In Manager, then we will focus on the Project context menu.
MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about VS extensibility: http://msmvps.com/blogs/carlosq/- Proposed As Answer byNathan Halstead [MSFT]ModeratorThursday, November 05, 2009 2:18 AM
- Marked As Answer byChao KuoMSFT, ModeratorThursday, November 05, 2009 3:40 AM
- Carlos
Thanks for clarifying the pattern, much appreciated.
I have modified and tidied the logic for the original addin as you suggested.
I also changed the addin code to do as the above example does, namely:
CommandBar bar2 = commandBars["Project and Solution Context Menus"]; CommandBarPopup con2 = (CommandBarPopup)bar2.Controls["Project"];
I rebuilt the addin, rebuilt the installer for the overall product (the addin is part of a software product) and did a test install on Vista x64 and XP x86.
The addin now works on both systems, so the original issue has been fixed.
Obvioulsy it was due to both the overall pattern AND the logic for getting at the "Project" context menu.
So the only problem I have now (and this is a lower priority, we can ship our beta with the above code in) is how exactly should we be getting at the "Project" menu?
Although the above code works, it may fail on some systems (could it ?) and you mentioned GUIDs.
Should we therefore use the GUID as a unique numeric key to get at the correct menu?
Thanks for you help once again.
Cap'n - Hello,
I'm glad that the original problem was solved.
About the problem of getting the "Project" menu, the usual way would be:
CommandBar projectCommandBar = DTE.CommandBars["Project"]
(using bar2.Controls["Project"] is bound to cause problems since controls don't have a name but a caption, and it is localized. Commandbars have names and are not localized)
I know for sure that there is at least one commandbar named "Project", which is used for Windows Forms projects. The problem can arise if Visual Studio has more than one commandbar named "Project", since in that case which one you get is not predictable. If you get the wrong one, you won't see your button because it is on another commandbar. Such additional "Project" commandbars can be contributed by some project types that you install on VS such as SilverLight, SharePoint, XNA and others. See:
INFO: Testing a Visual Studio add-in against new Visual Studio tools or extensions from Microsoft
To verify if this is the problem:
- Test the add-in on a VS where it works, so you know that your code is correct and the button is there.
- Then, on the system where the button is not there, run this code so you verify that the problem is caused by a second commandbar with the "Project" name:
foreach (CommandBar commandBar in DTE.CommandBars)
{
if (commandBar.Name = "Project")
{
MessageBox.Show("I am commandbar with name 'Project'");
}
}
If you get more than one MessageBox, then that is the problem. In that case you need to use a service (IVsProfferCommands.FindCommandBar) from the SDK, which sounds scary but it can be done from an add-in, so start familiarizing with the approach:
HOWTO: Get a Visual Studio service from an add-in
http://www.mztools.com/articles/2007/MZ2007015.aspx
and the details of the solution are explained here:
Using IVsProfferCommands to retrieve a Visual Studio CommandBar
http://blogs.msdn.com/dr._ex/archive/2007/04/17/using-ivsproffercommands-to-retrieve-a-visual-studio-commandbar.aspx
and here:
Finding GUID / menuid for Project in Solution Explorers
http://social.msdn.microsoft.com/Forums/en/vsx/thread/4e1ee28f-d3f5-4702-b9ac-63c08d00a989
MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about VS extensibility: http://msmvps.com/blogs/carlosq/- Proposed As Answer byNathan Halstead [MSFT]ModeratorWednesday, November 04, 2009 2:09 AM
- Marked As Answer byChao KuoMSFT, ModeratorThursday, November 05, 2009 3:40 AM


