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.