It has been a long time since my last post here, and for a good reason. Since I released MZ-Tools 8.0 for Visual Studio 2015 last July 1 (converted from add-in to package), I have been very busy the last months to achieve a new huge milestone:
Announcing unified MZ-Tools 8.0 for Visual Studio, VBA (64-bit/32-bit), VB6 and VB5
From the extensibility point of view, MZ-Tools 8.0 is a unique piece in the world, AFAIK: it provides the same feature set for four different IDEs (Visual Studio, VBA editor, Visual Basic 6.0 and Visual Basic 5.0) with different extensibility APIs reusing the same code at binary level:
MZ-Tools 8.0 for Visual Studio (as package):
MZ-Tools 8.0 for Visual Studio (as add-in):
MZ-Tools 8.0 for VBA:
MZ-Tools 8.0 for VB6:
MZ-Tools 8.0 for VB5:
As you can see in the images, in all cases there is the same big MZTools8PlugIn.dll file, which contains the feature set, and smaller host adapter dlls that “plug” the features into the IDEs. If you are interested, I outlined the strategy to achieve this in this other post.
Although this article applies only to the old Visual Studio 2008, I am writing it for completeness of the body of knowledge that I have built over the years about add-ins. And if you are a VSX developer who cares about your users/customers, chances are that your extension still supports Visual Studio 2005 / 2008 (which are widely used yet at companies because for some reason developers don’t always migrate to the latest version. See How many old Visual Studio versions does my add-in/package have to support?). Recently I encountered an issue in one of my scripts that remove add-in commands, and it took me a while to find that the root cause was a bug in Visual Studio 2008:
BUG: Visual Studio 2008 crashes if unloaded when an add-in executes DoEvents statement on startup
If you were in the VSX world in the VS 2008 era of add-ins, you may remember that there was another bug that caused VS 2008 to crash on startup:
BUG: Add-in causes Visual Studio 2008 crash when loaded
Fortunately both bugs were fixed in next releases.
My new article is to document a bug that a customer of my MZ-Tools add-in reported some days ago, and that is caused by a bug (or problem) in the automation model (EnvDTE). It happens in the following scenario:
- Using the latest versions of Visual Studio (it doesn’t happen with old versions such as VS 2005, 2008)
- You add a .sql file to a project that doesn’t support .sql files, for example to a C# project. The problem doesn’t happen with projects that support .sql files such as database projects.
- If you open the project item to get its (invisible) window, get its text document, modify it and close the window saving the changes, the changes are not persisted to disk.
You have the details and code to reproduce the problem here:
PRB: Window.Close doesn’t save changes if requested for .sql files in non-database projects
I am not sure if this is a bug or not, because the project doesn’t support .sql files. There is an easy workaround, though. You can call:
A question in the VSX forum has intrigued me enough to test a creative solution: to intercept a command execution to re-execute it with an input parameter that you can retrieve when the execution finishes. This can be useful in a scenario where a command can be executed asynchronously twice and you want to know which/when each execution is finished:
HOWTO: Pass parameters programmatically to a command from a Visual Studio add-in
There is a sticky VSX FAQ post in the Visual Studio Extensibility (VSX) Forum since more than one year ago that is intended to collect the FAQs about development of Visual Studio extensions (packages, add-ins, templates, isolated shell, etc.). Unfortunately there aren’t many FAQ entries in each category, which is a pity. Today I have added tons of them in the “Add-ins” category, my area of expertise, pointing to the most useful HOWTO articles of my MZTools Articles Series. Hopefully they are useful for people before posting in the forum:
This afternoon I was writing an add-in for a new MZ-Tools series article and while I thought that EnvDTE.Window.Kind returned a Guid to identify the kind of a toolwindow, it actually returns the literal “Tool” for all toolwindows.
I revisited the article:
HOWTO: Getting information about Visual Studio windows from an add-in
and I noticed that the information was incorrect and that in Visual Studio .NET 2003, 2005, 2008 and 2010 (I am unable to test Visual Studio .NET 2002 on Windows 7 64-bit), EnvDTE.Window.Kind doesn’t return a Guid but “Tool” for toolwindows and “Document” for document windows. It is the EnvDTE.Window.ObjectKind property which returns such Guid. So, I have updated the article to fix the misleading information.
MSDN has a nice URL mechanism to reference several Visual Studio versions of the same documentation, for example:
Automation and Extensibility for Visual Studio (Visual Studio 2010)
Automation and Extensibility for Visual Studio (Visual Studio 2008)
Automation and Extensibility for Visual Studio (Visual Studio 2005)
Long overdue, but today I have updated the MZ-Tools Resources about Visual Studio .NET extensibility page with links to Visual Studio 2010 extensibility resources (downloads of SDKs, documentation, etc.).
I think I forgot to blog about the resolution of this bug:
devenv.exe /resetaddin doesn’t fully reset the add-in
whose Microsoft Connect bug report is:
The problem is, you see, that devenv.exe /resetaddin doesn’t remove permanent commandbars, only commands and buttons on commandbars. That means that if you are using permanent commandbars and not temporary ones, at the very least your uninstaller needs to take care of removing permanent commandbars explicitly because devenv.exe /resetaddin won’t do it. This can be done creating an instance of EnvDTE.DTE, locating the commandbar and calling EnvDTE.DTE.Commands.RemoveCommandBar (see HOWTO: Removing commands and UI elements during Visual Studio .NET add-in uninstallation). And not only the uninstaller: when debugging you may need to remove the commandbar too because VS 2005 and higher fire a new UISetup phase each time that you debug, so you get duplicated commandbars.
The temporary vs permanent approaches for commandbars is a nightmare for two reasons:
- First, their own existence. A single approach would be much better. And the temporary one would be preferable. The reason for the permanent one is that for add-ins with a large number of commandbars and buttons, it takes less time to persist the UI on disk and retrieve it from there when launching VS than recreating the UI. I think that there aren’t many add-ins with lots of commandbars and buttons (certainly my MZ-Tools add-in is one with such large number of buttons, but I keep using the temporary approach). But if performance is a problem, then it is better to fix that rather than inventing a new approach. Some ideas for Microsoft to increase the performance of add-ins using the temporary approach are:
- Provide a EnvDTE.Commands.Exist(commandName) method to test if a command exists or not (to create it only if it doesn’t exist). Currently you have to call the Item method which causes an (expensive) exception if the command doesn’t exist.
- Provide a EnvDTE.AddInCommands collection that returns only the commands of add-ins, not the hundreds of commands of Visual Studio.
- Make EnvDTE.Command.AddControl to behave as follows: if you are adding a button to a menu, set the CommandBarButton.Style to msoButtonIconAndCaption (VS already does this), but if you are adding it to a toolbar, set the CommandBarButton.Style to msoButtonIcon since it is more likely that style (without caption) on a toolbar. Currently VS defaults to msoButtonIconAndCaption also in this case, which means that after getting the CommandBarControl, you have to cast it to CommandBarButton and then change the Style property, which means that the button needs to be redrawn (a performance hit). I already reported this two years ago to no avail: Wrong default vsCommandStyle for CommandBarButtons created from add-ins. Even more, Visual Studio doesn’t work as supposed to do: Problems with ContextUIGUIDs and vsCommandDisabledFlagsValue in EnvDTE.Commands.AddNamedCommand (a bug yet to be acknowledged and fixed).
- Simplify the way of getting transparent pictures for commands. The transparent color has been RGB=0,254,0 for long time which means that a color remapping must be performed. Hopefully this has been addressed in VS 2010 with support for 32-bit bitmaps with transparency in the alpha channel.
- Second, the APIs are not consistent, which creates a lot of confusion. If both approaches need to exist, at least they would have to use the same methods with just one boolean parameter indicating if a commandbar is permanent or temporary. Currently you have:
- CommandBars.Add to add temporary commandbars
- CommandBar.Delete to remove temporary commandbars
- Commands.AddCommandBar to add permanent commandbars (even if the CommandBars.Add method above has a Temporary parameter!)
- Commands.RemoveCommandBar to remove permanent commandbars
Anyone can see that that mix of methods to add commandbars is bound to cause problems (why is there a AddCommandBar method in the Commands collection?) and certainly it is: the bug of devenv.exe /resetaddin can’t be fixed because the Commands.AddCommandBar method lacks a fundamental piece of information: the AddIn instance that is adding the commandbar. Without that information the IDE doesn’t know which add-in is the owner of a permanent commandbar and therefore can’t remove it.
Two years after I reported it, it seems that MS finally understood that this was a bug and I have been notified today that it is no longer closed as “By design” but as “Fixed”:
Standard user unable to load/unload COM-based add-ins registered for all users with the VS 2008 Add-in Manager on Windows Vista
This bug has plagued all of us developing add-ins with COM-registration (rather than .AddIn XML registration) for all users (not for current user) for backwards compatibility with VS.NET 2002 and/or 2003. Too bad that the fix comes so late that I doubt there will be a fix for VS 2008, only for VS 2010…but anyway, it will make VS 2010 a better product.
Another minor bug in the C# file code model that I have reported to Microsoft:
BUG: EnvDTE.CodeElement.Name doesn’t return interface name for explictly implemented C# interface events