VS 2010 and the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface

This is going to be messy, so step by step:

Imagine that you are developing an add-in that uses a temporary user interface (not permanent one), and creates a couple of commandbars of kind “toolbar”, and many commandbars of kind “popup” (with submenus).

Since it uses a temporary user interface, it must delete those commandbars when unloaded.

For that purpose, it must keep a reference to each created commandbar at class level (all this is explained in the article of the previous link).

Since it creates lots of commandbars, rather than keeping each one individually, they are stored in a class-level collection. Of which type?

CommandBars of kind “Toolbar” have the “Microsoft.VisualStudio.CommandBars.CommandBar” type but commandbars of kind “popup” have the Microsoft.VisualStudio.CommandBars.CommandBarPopup” type, so both can’t be stored in the same typed collection, but we would like to use a single collection, not two, or not a collection of System.Object.

Fortunately the Microsoft.VisualStudio.CommandBars.CommandBarPopup type has a CommandBar property, and since the Microsoft.VisualStudio.CommandBars.CommandBar type has a Parent property (typed as System.Object, that should return a Microsoft.VisualStudio.CommandBars.CommandBarPopup actually), you can use a generic typed collection List<Microsoft.VisualStudio.CommandBars.CommandBar>, which stores the CommandBar of toolbars, and the CommandBarPopup.CommandBar of popup commandbars.

When the add-in is unloaded, it iterates the commandbars of the collection to delete them:

  • If the CommandBar.Type is MsoBarType.msoBarTypeNormal, the commandbar is a toolbar and can be deleted calling the Delete method.
  • If the CommandBar.Type is MsoBarType.msoBarTypePopup, the commandbar is a popup so we get its Parent property (which is a System.Object) and we cast it to Microsoft.VisualStudio.CommandBars.CommandBarPopup. Then, we call the Delete method of CommandBarPopup.

(As I mentioned, my MZ-Tools add-ins seems to use every conceivable technique provided by the Microsoft.VisualStudio.CommandBars API)

However, this approach that worked perfectly in VS 2005/2008, doesn’t work in VS 2010. Why?

It happens that VS 2010 uses WPF-based commandbars, but add-ins for VS 2010 still use the Microsoft.VisualStudio.CommandBars Interop (ActiveX) assembly for backwards compatibility.

That interop assembly provides the Microsoft.VisualStudio.CommandBars.CommandBarPopup type, which is actually an interface (although it doesn’t follow the convention for interface names), not a class. The same happens with the CommandBar and CommandBarControl types, which are interfaces, not classes. The CommandBarButton type is a class, though. To complicate things, you have the CommandBars class and the _CommandBars interface… anyway:

In VS 2005/2008, which use native (COM) commandbars, the classes that actually implement those interfaces are native, and from a managed add-in you see them through a System.__ComObject which is the Runtime Callable Wrapper (RCW) that you get when you don’t have an interop assembly. But since they implement the interfaces provided by the Microsoft.VisualStudio.CommandBars assembly, the add-in works fine.

In VS 2010, which uses managed (.NET) WPF commandbars, the classes that actually implement those interfaces are managed and are provided by the Microsoft.VisualStudio.PlatformUI.Automation namespace in the managed (.NET) Microsoft.VisualStudio.Shell.UI.Internal.dll assembly (in one of the VS 2010 folders).

When you create a commandbar popup calling:

Microsoft.VisualStudio.CommandBars.CommandBarControl commandBarControl;
commandBarControl = parentCommandBar.Controls.Add(MsoControlType.msoControlPopup);

you get some class instance that implements the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface. So you can do this cast:

Microsoft.VisualStudio.CommandBars.CommandBarPopup commandBarPopup;
commandBarPopup = (Microsoft.VisualStudio.CommandBars.CommandBarPopup) commandBarControl;

In VS 2010, the actual class that implements that Microsoft.VisualStudio.CommandBars.CommandBarPopup interface is the Microsoft.VisualStudio.PlatformUI.Automation.CommandBarPopup._Marshaler class.

However, when you do this:

commandBarPopup.CommandBar.Parent

in VS 2005/2008 you get an object that implements the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface (as expected), so the approach described in the beginning of this post worked.

In VS 2010 you get an instance of the Microsoft.VisualStudio.PlatformUI.Automation.CommandBarPopup class, not of the Microsoft.VisualStudio.PlatformUI.Automation.CommandBarPopup._Marshaler class that you got when creating it.

It happens that the Microsoft.VisualStudio.PlatformUI.Automation.CommandBarPopup class doesn’t implement the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface. It implements IMarshaledObject<CommandBarPopup> instead. It also has a Marshaller property that would return the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface implementation.

So, the approach that worked with VS 2005/2008 doesn’t work with VS 2010.

Since you can’t cast or get the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface and an add-in targeting VS 2005/2008/2010 with the same dll can’t include a reference to the Microsoft.VisualStudio.Shell.UI.Internal.dll, keeping this approach the only way would be to call its Delete method would be through Reflection.

10 thoughts on “VS 2010 and the Microsoft.VisualStudio.CommandBars.CommandBarPopup interface”

  1. Hi Carlos,

    I’m testing the VS Beta 2 milestone as you are except I’m working with the C++/COM interface.

    Creating Toolbars with Command Popups can be created in the IDE correctly. However the commands being generated by my code are set to the style PictAndText. So I’m anticipating the popup to only show icons, and menus to show icons and text.

    Hang on, the popup now shows Icon and Text (different behaviour to pre-Beta 2) instead of just an Icon. (The command is associated with a menu as well).

    I then modified the command creation style to be just icon. The toolbar now looks correct, however the menu now only shows just text and no icon.

    Back to the drawing board?

  2. Hello Beth,

    I haven’t played yet with the button style in the command creation, I will do in the next days. If you think there is a bug with, please isolate it and report it to Microsoft Connect so they can fix it in next builds.

    FWIW, I have played with CommandBarButton.Style and there is a bug that I reported and it will be fixed in builds after beta 2:

    VS 2010 Beta 2 Bug: CommandBarButton.Style not honored, always Icon + Caption
    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=500403

  3. Ho carlos, I am facing a problem related to command bars. I had the following code which works in Vs2005/2008. Basically i am trying to get a commandbar given a commandbarcontrol. Following is the code which worked in VS2005/2008:
    Microsoft.VisualStudio.CommandBars.CommandBars cmd = ((EnvDTE._DTE)applicationObject).CommandBars as Microsoft.VisualStudio.CommandBars.CommandBars;

    IEnumerator toolbarEnum = cmd.GetEnumerator();
    while (toolbarEnum.MoveNext())
    {
    Microsoft.VisualStudio.CommandBars.CommandBar toolBarVal =
    (Microsoft.VisualStudio.CommandBars.CommandBar)toolbarEnum.Current;
    if (toolBarVal != null)
    {
    CommandBar mainMenu = cmd[“Menubar”];

    IEnumerator enum2 = mainMenu.Controls.GetEnumerator();
    while (enum2.MoveNext())
    {
    CommandBarControl c1 = (CommandBarControl)enum2.Current;

    //if (c1.Caption.Replace(“&”, “”).Equals(“Schema”)) MessageBox.Show(“Schema”);
    if (c1.Caption.Replace(“&”, “”).Equals(toolbarName) || c1.Caption.Equals(translatedtoolbar) || c1.Caption.IndexOf(translatedtoolbar) > -1){
    CommandBar commandBar22 = (CommandBar)c1.GetType().InvokeMember(“CommandBar”, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty, null,c1, null);

    But in VS2010 when it hits the (CommandBar)c1.GetType().InvokeMember, i get an exception “Missing Method exception Method ‘Microsoft.VisualStudio.PlatformUI.Automation.CommandBarPopup+_Marshaler.CommandBar’ not found”
    Do you have any idea how to get around this bug?

    Thanks,
    Kalpana

  4. Thanks Carlos. That helped. I cast the CommandBarControl to CommandBarPopup and got the commandBar by doing CommandBarPopup.CommandBar.

    I think the information that you posted on your blog is very helpful.

  5. Hi Carlos,
    Just wanted to check if you have come across a problem that i am going to describe.
    Basically i have a desginer that i am trying to open and this designer has an embedded editor. Following is the code:

    IVsUIShellOpenDocument shell = (IVsUIShellOpenDocument)DesFMPkg.Instance.GetService(typeof(IVsUIShellOpenDocument));
    IOleServiceProvider sp = (IOleServiceProvider)DesFMPkg.Instance.GetService(typeof(IOleServiceProvider));
    Guid editorType = GuidList.guidMultiViewEditor;
    Guid logicalView = GuidList.guidDesignView;
    int hr;
    if (des.HasCodeEditorView)
    {
    IVsSolution vsSolution = (IVsSolution)DesFMPkg.Instance.GetService(typeof(SVsSolution));
    object obj;
    hr = vsSolution.GetProperty((int)__VSPROPID.VSPROPID_IsSolutionOpen, out obj);
    if (!(bool)obj)
    {
    hr = vsSolution.CreateSolution(string.Empty, string.Empty, (uint)__VSCREATESOLUTIONFLAGS.CSF_TEMPORARY);
    }
    }

    this.m_designer = des;
    string caption = “”;
    // Find the item id assigned to this document by the project.
    uint pitemid = VSConstants.VSITEMID_NIL;
    string moniker = des.Moniker;

    VSVirtualProject project = DesFMPkg.Instance.GetService(typeof(VirtualProject));
    IVsUIHierarchy hier = project as IVsUIHierarchy;
    IVsProject proj = project as IVsProject;

    hr = project.AddDesigner(des, out pitemid);
    ErrorHandler.ThrowOnFailure(hr);
    hr = shell.OpenSpecificEditor(
    (uint)0,
    moniker,
    ref editorType,
    null,
    ref logicalView,
    caption,//node.Caption,
    hier,
    pitemid,
    System.IntPtr.Zero,
    sp,
    out editorFrame);

    if (ErrorHandler.Succeeded(hr)) {
    editorFrame.SetGuidProperty((int)__VSFPROPID.VSFPROPID_InheritKeyBindings, ref GuidList.guidCmdUI_TextEditor);
    if (this.DontShowWindow == false)
    rrorHandler.ThrowOnFailure(editorFrame.Show());
    }

    on the EditorFrame.show, i get AccessViolationException. “Attempt to read to write protected memory”.
    Do you know if this is some known problem in Vs2010 Beta2?

    Thanks,

  6. Hi Carlos,

    On a related topic, do you have an idea why calling CommandBarControls.Add(…) takes 2 to 10 seconds and leak 30MB on VS2010 Beta2, while it work fine on 2005 and 2008? The code looks like that:

    CommandBarPopup physicalPopup
    ….
    var controls = physicalPopup.Controls;
    var buttonAdded = controls.Add(MsoControlType.msoControlButton, 1, “”, System.Reflection.Missing.Value, true);

Comments are closed.