Some days ago I received an angry complaint from a prospect of the trial version of my MZ-Tools add-in, who uninstalled it because, somehow, he didn’t want the add-in loaded at startup during the evaluation period but he wanted to load it manually from time to time… Anyway, it happens that my MZ-Tools add-in still uses COM-registration because it shares code and setup with the versions for VS.NET 2002/2003, and therefore is subject to the couple of bugs of the Add-In Managers of VS 2008 and 2005. Of course the blame ended on the Microsoft side but too late, I’m afraid. It is unfortunate to lose sales because of these little problems… so today I started to investigate the migration from COM-registration to XML-registration for the versions of MZ-Tools for VS 2005 and 2008. And after some hours of internal refactoring needed, the .AddIn file created, I was ready to debug the add-in, when I got:
“No such interface supported” (80004002)”
How could that be? I have spent years creating add-ins for samples and I never got that error. A search in Google returned no useful information. At first I thought that it was related to the fact that I didn’t want my add-in, which is written in VB.NET, to have a root namespace (for those of you using C#, in VB.NET you can set a project-level property with the root namespace and avoid writing it in each code file), because I don’t want my commands to be of the form “MZTools.Connect.MyCommand” but of the form “MZTools.MyCommand”, which is possible as I explained in this article.
After some minutes investigating, I discovered that my AssemblyInfo.vb file has the ComVisible attribute set to False, instead of the default True that the add-in wizard creates:
because it doesn’t make sense to expose to COM the classes of an add-in, except the Connect class. And here was the problem: while I had the <ComVisible(True)> attribute attached to the Connect class, when I removed the <Guid(…)> and <ProgId(…)> attributes, which are not required for XML-registration, I removed too the <ComVisible(…)> attribute, because I thought that it was not required for XML-registration, but it happens that it IS required, go figure! I think we’ll never get rid of COM in Visual Studio extensibility in a million of years, no matter how hard Microsoft tries to disguise it…
Recently I was playing with the FileCodeModel of a ProjectItem that used conditional compilation to provide some methods in “Debug” configuration (but not in “Release” configuration) such as:
Public Sub Method1()
#If DEBUG Then
Public Sub Method2()
Public Sub Method3
Which code elements (methods) would return the FileCodeModel in such case when the active configuration is “Release”? It would honor the conditional compilation? It happens that yes, for the “Release” configuration the FileCodeModel returns methods #1 and #3, but not method #2. Is that good or bad? I tend to think that most of the time you will want that behavior. For example, if you are writing an add-in to review code elements (for dead code, etc.), you only want to deal with code elements that actually exist for your current configuration. In other scenarios this is not so good. For example, if you are writing a feature to sort code elements (by name, scope, etc.) you don’t want to leave methods out. But overall I think that Microsoft did it right honoring the conditional compilation.
I have always wondered why the IDTExtensibility2 interface of the Visual Studio host to load (or connect) add-ins is not as simple as just two methods: OnLoad and OnUnload, or if you like the “connection” term, just the OnConnection and OnDisconnection methods. The original IDTExtensibility interface of Visual Basic 5/6 from 1997/1998, which offered four methods (OnConnection, OnDisconnection, OnAddInsUpdate and OnStartupComplete), rather than being simplified in the transition to VS.NET 2002, got a new life as IDTExtensibility2, with even a new method (OnBeginShutdown). All of them seem totally unnecessary to me, let see why:
- OnAddInsUpdate: this is perhaps the most useless method. I have yet to see an add-in that makes some use to it. Anyone? It’s good that you can access other add-ins from yours using the DTE.AddIns collection, and even that you can unload some add-in using the AddIn.Connected property (long time ago my MZ-Tools add-in had to unload a conflicting add-in before an operation to avoid a crash and reload it afterwards), but there is no much need to get notified when an add-in is loaded/unloaded. If there is, some event in the DTEEvents class would be better than a method of an interface that you have to empty-implement.
- OnStartupComplete: this method is called when the add-in is loaded on startup and the IDE is fully initialized (because when the OnConnection method was called, it was not fully initialized). I have yet to see an add-in that needs to behave differently when it is loaded on startup, or sometime later through the Add-in Manager, or that wants to do something before the IDE is not fully initialized. The MSDN documentation states:
“On occasion, OnConnection does not occur correctly, such as when an add-in is loaded, but a component required by an add-in has not yet loaded. This is unusually due to the fact that Visual Studio has not yet started completely.”.
In such case I would prefer my OnConnection method to be called when the IDE is fully loaded, not before that. The behavior of OnConnection / OnStartupComplete is so confusing that I had to write an article to show how to do it right, tired of seeing wrong code in the forums.
- OnBeginShutdown: this is another useless method. The MSDN documentation states:
“Although a shutdown of Visual Studio might be canceled, the OnBeginShutdown method cannot be canceled. As a result, add-ins should assume that all shutdown events occur and perform any cleanup routines accordingly.”.
So, when Visual Studio thinks it is going to shutdown, it calls this method and your add-in has to cleanup. What is going to do then when it receives the OnDisconnection call?. But wait! The shutdown could be cancelled (for example, if there are dirty documents, the user is prompted to save them and she cancels), but, alas, your add-in already did the cleanup… I don’t see the point. Either this method is useless, or at the very least it should be called when the IDE is totally sure that it is going to shutdown (after giving the user the last chance to cancel, to begin with).
BTW, the ext_ConnectMode.ext_cm_UISetup flag of the OnConnection method is so special, the add-in is loaded (or connected) for such a special purpose (to create the commands and permanent user interface of the add-in) that it truly would deserve its own OnUISetup method.
So, if correct initialization/shutdown of add-ins is so tricky, it is no wonder that developers entering the Visual Studio automation world find it so hard.
FWIW, Microsoft recognized this need of simplification in the new managed add-in models for Microsoft Office using Visual Studio Tools for Office (VSTO) 2005 (see the Startup/Shutdown events in VSTO 2005), or in the new IManagedAddIn interface of Office 2007 with its Load/Unload methods. I am not familiar with those models but seeing the docs you realize that you have to deal with just one event on load and just one event on unload. A good step in the right direction.
Almost a couple years ago I wrote an article The Add-In Manager of Visual Studio 2005 got broken…and won’t be fixed for SP1 and while Visual Studio 2008 fixed that problem, it has introduced a new one when running on Windows Vista, and guess what, VS 2008 SP1 doesn’t fix it either, so I guess we will have it until the next version of Visual Studio… Again, the problem happens with COM-based add-ins, not with add-ins using a .AddIn XML file. I guess that most developers of commercial add-ins still need to support Visual Studio .NET 2003 (my MZ-Tools add-in even supports Visual Studio .NET 2002) and they may want to use a single code base for the code and the setup, so they still use COM registration for add-ins for Visual Studio 2005/2008 (at least this is my case), so it would be good to get this bug fixed.
Here is the article explaining the bug:
BUG: Standard user unable to load/unload COM-based add-ins registered for all users with the VS 2008 Add-in Manager on Windows Vista
And here you have the bug report on the Microsoft Connect web site to vote for it:
Last year I wrote a quite popular article HOWTO: Troubleshooting Visual Studio and Office add-ins, to diagnose many common problems with add-ins for Visual Studio or even Office. One invaluable tool to diagnose a whole range of problems with an add-in setup or the Add-In Manager of Visual Studio is the Process Monitor (ProcMon) tool, formerly developed by SysInternals and now part of Microsoft Technet. My MZ-Tools add-ins for Visual Studio (all versions), VB6, VB5 and VBA are installed and registered smoothly for tons of users every week, but from time to time I get a support incident about the setup failing due to something wrong on the user machine and I diagnose the problem requesting the user to run the tool and sending the trace back to me. While the Process Monitor tool applies to any developer, not just add-in developers, not everybody is familiar with it, so I wrote this article to explain how to use the tool and to refer my users to it. As an example, I will show why Visual Studio 2008 fails to recognize .addin files when stored in the folder “%ALLUSERSDOCUMENTS%\Microsoft\MSEnvShared\AddIns” that was introduced by Visual Studio 2008.
HOWTO: Using the Process Monitor (ProcMon) tool to diagnose Visual Studio add-ins problems
I have updated the article that I wrote back in January about default folders to place .AddIn registration files for add-ins to include actual examples for Windows Vista since the original article only showed Windows XP examples. I have also provided better information about how to get the actual folder names correctly.
Overall the naming of Windows Vista folders is perfect because:
- It doesn’t localize folder names
- It uses meaningful names such as:
- “C:\Users\<username>\AppData\Roaming” while Windows XP used “C:\Documents and Settings\<username>\Application Data” (how do you remember that it is roaming?)
- “C:\Users\<username>\AppData\Local” while Windows XP used “C:\Documents and Settings\<username>\Local Settings\Application Data”
but because of backwards compatibility with Windows XP it uses junction points and all this folders stuff is really a mess. Anyway, the updated article:
INFO: Default .AddIn file locations for Visual Studio add-ins
When developing commercial add-ins for Visual Studio, chances are that you will have to test it with international versions of Visual Studio to ensure that your tool works fine, say, in Visual Studio Spanish or German or French or Japanese or Chinese or whatever. While Visual Studio .NET has always supported multilingual interface (via “Tools”, “Options” window, “Environment”, “International Settings” section, “Language” combobox), this requires you to install the whole localized version of VS on top of the existing (English) one to have another language in that combobox. And here it comes my complaint: you can almost watch an entire movie while Visual Studio is installed, and lately even when a VS service pack is installed (I had time yesterday to go to run to the park and get the shower before VS 2008 SP1 got installed), so you can spend a whole day installing all those languages.
Windows Vista offers an elegant solution: the core of the product is language-neutral and language-packs are provided for the UI. Supposedly the core + language packs are much more small and manageable than the multiple localized versions. I wish Visual Studio used the same architecture. It would even fit in a single DVD for distribution purposes and the setup would prompt you which language(s) to install.
During the past five years I have written about 120 articles on my MZ-Tools web site about automation and extensibility of Visual Studio, most of them intended for add-ins, some usable from macros too and some other using services of the SDK (rather than the automation model) from an add-in.
In the next few weeks I do plan to include relevant links to them in the Community Content section at the bottom of each topic in the MSDN documentation, so that add-in developers not aware of them or not aware of my blog where I announce new articles can reach them. I have started today with some links in some topics. See how the “Common Environment Object Model Examples” MSDN topic is enriched now:
Common Environment Object Model Examples
Of course, other topics have a much more narrow list of articles:
How to: Expose an Add-in on the Tools Menu (Visual Basic
PS: One problem of the Community Content editor provided by MSDN is that it doesn’t have a button to insert hyperlinks, so they were just text that you have to copy and paste in the address bar of the browser. I am not sure if this is “by design” to disallow HTML content (spam?) or that someone forgot to provide that button, but it happens that you can paste HTML content in the editor, so I managed to include actual hyperlinks…
In my previous post I talked about the convoluted build configuration automation model (EnvDTE/EnvDTE80). While I had it fresh in my head, I used the “Export to image” feature of my brain (OK, I used Visio) and here is a graphical representation of it. It is a model of objects (not classes) in memory, where each ellipse is an object and its text inside is the class of the object. The continuous arrow lines are properties and the the text of the line is the property name. The dotted lines are not properties but indirect pointers. It should be clear now that the object model for solution configurations is quite different than the one for projects (one is modeled as a collection while the other as a matrix), which is a pity.