AFAIK, add-ins can’t use “global” exception handlers using AppDomain.UnhandledException, I think that because maybe all add-ins are loaded in the same AppDomain (not sure) and therefore it wouldn’t be suitable that an add-in handles the unhandled exception of another add-in… Anyway, I use exception handlers all around the code of my MZ-Tools add-in. Although I use a single method to treat the exceptions (showing an error to the user with a “Send Report” button, etc.), I made two errors that I am now (costly) fixing for next versions:
– The bug report misses important information about the IDE where the add-in causes the problem, including the IDE version. While in MZ-Tools 6.0 I use a different add-in assembly for each target IDE (so I can actually know the version), future versions of MZ-Tools will use a single add-in dll to target multiple IDEs.
– That method didn’t receive the DTE object and when I modified it to receive it, I realized that a lot of methods that were trapping exceptions didn’t have that DTE object to pass, because they were “helper” static (shared in C#) methods, so I had to do a lot of refactoring for every method to get access to the DTE object that the Connect class owns.
I have summarized what I consider good practices in this new article:
HOWTO: Handle exceptions in a Visual Studio add-in.
After three whole months of procrastination about the MZ-Tools Articles Series and a lot of hesitation about continuing or giving up (I am continually struggling with day job, nights/weekends, family, spare time, some needed sport, this blog, the MSDN VSX forum, the MVP program, the MZ-Tools add-in, priorities balancing, etc. and even PS3 Top Spin 3 tennis gaming and a planned switch to Apple / iMac), it seems that today I found some desire to write a new (small) article about a question that appears from time to time in the forums:
HOWTO: Locate the index of a CommandBarControl on a Commandbar to add a control or menu before or after it from a Visual Studio add-in.
More to come about internationalization issues in Visual Studio add-ins that I am facing these days…
One of the customers of my MZ-Tools 6.0 for Visual Studio .NET add-in reported some days ago that the setup was showing the following error when installing MZ-Tools for Visual Studio 2010 (RC) (it worked fine for other IDE versions):
“The assembly ‘VSLangProj80, version 220.127.116.11, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ should be present in the Global Assembly Cache (GAC) but it has not been found there, maybe the uninstallation of some other product remove it”
It happens that it is not clear for some people (even within Microsoft) that EnvDTE.dll, VSLangProj,dll, etc. should not be included in the setup of Visual Studio add-ins, so some people were including those dlls in their add-in setups, with the undesired consequence that somehow those dlls were being removed from the GAC when uninstalling some add-ins. When I got tired of bug reports related to this, I made the setup of my add-in to check the existence of some required Visual Studio assemblies such as EnvDTE.dll, etc. and to provide the customer with a procedure to restore them (omitted for brevity in the message above) copying them from the Visual Studio folders to the GAC.
So, I got my customer to check the VSLangProj80.dll in the GAC and… it was there. Then I noticed that VSLangProj80.dll has a dependency on VSLangProj2.dll that the setup was not checking. But that dll was also in the GAC. Then I noticed that to check the existence of a DLL in the GAC my setup was trying to load the dll using System.Reflection.Assembly.Load(assemblyName) and if the load failed with any exception, then it assumed that the assembly was not in the GAC. So I guessed correctly that the error message was misleading, I changed the setup to show the message above only if the exception was a System.IO.FileNotFoundException, and to show the actual exception otherwise. Then we got this other error:
“System.IO.FileLoadException: Mixed mode assembly is built against version ‘v1.0.3705’ of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information”
Then I learned what a “mixed mode assembly” is and the useLegacyV2RuntimeActivationPolicy attribute that needs to be used in a .config file to fix that issue in CLR 4.0, but I will let Mark Miller (Marklio) to explain it in full detail and to the MSDN documentation the short version.
There is something that I haven’t solved yet and it is why I was unable to reproduce the problem on my computer when the .config file didn’t have yet that attribute, but anyway the problem was solved for my customer.
Today I installed the latest 3.0 version of the Windows Installer XML (WiX) toolset (3.0.5419.0), which integrates with both Visual Studio 2005 and 2008 and provides a new project type and project template to create WiX setups. When I clicked the File, New Project menu, WiX project type, it worked fine in VS 2005. However, it caused the following error in VS 2008:
“.wixproj cannot be opened because its project type is not supported by this version of the application. To open it, please use a version that supports this type of project.”
After searching the web for a while to no avail, I finally noticed that this problem was caused because my shortcut to VS 2008 included /SafeMode after devenv.exe. When the shortcut didn’t include that command-line switch, it worked fine. That switch disables the Add-In Manager and prevents add-ins and 3rd party packages (such the WiX one) being loaded. I had it because I have my MZ-Tools add-in marked to load on startup (to avoid loading it by hand through the Add-In Manager each time I debug it), but I don’t want it to be loaded the very first time that I open VS to load its project, to avoid the problem PRB: ‘Could not copy temporary files to the output directory’ error building Visual Studio .NET add-in. Another way to prevent it is to press the left Shift key while VS is started, but that approach doesn’t work well on Windows 7 and VS 2010 will not support it, as explained in the article, so using /SafeMode will be the only approach.
Now, it would be nice if VS showed a more helpful message such as:
“.wixproj cannot be opened because its project type belongs to a 3rd party package that is not allowed to load when the Visual Studio process (devenv.exe) is launched with the /SafeMode switch.”
Although as I have posted many times I personally use and recommend the free InnoSetup script-based tool to create the setup of my MZ-Tools family of add-ins (see HOWTO: Create a setup for a Visual Studio 2005 / 2008 add-in using Inno Setup), there are people who prefer MSI-based setups.
The tricky part of installing XML-based add-ins is always to decide where to put the .AddIn file (see INFO: Default .AddIn file locations for Visual Studio add-ins) and then to modify the <Assembly> value to point to the location where the user has decided to install the add-in dll (typically in the C:Program Files folder). This second part is always omitted from the Microsoft documentation and posts, because it requires a custom action, which is complicated because it requires code.
For this purpose, Windows Installer XML (Wix) greatly simplifies the task. WiX is a toolset that builds Windows installation packages from XML source code. Although I haven’t started to play with WiX yet, I have found that Alex Shevchuk has a great article series about moving from MSI to WiX, and specially interesting is the part 25 about updating XML files using XmlFile, which can be used to change the <Assembly> value.
I will try to find time to write a HOWTO article about this some day, but meantime, if you are interested, you can download the VS 2008 SDK 1.1 and go to the file C:\Program Files\Microsoft Visual Studio 2008 SDK\VisualStudioTeamSystemIntegration\Test Tool Extensibility\VsIdeHostAdapter\Installer\VsIdeTestHost.wxs, which uses this approach (bold line):
<!-- Directories -->
<!-- One of the per-machine directories Visual Studio searchings for .Addin files -->
<Directory Id="MSCommonAppData" Name="MS" LongName="Microsoft">
<Directory Id="MSEnvCommonAppData" Name="MSEnvSH" LongName="MSEnvShared">
<Directory Id="DIR_ADDINS" Name="Addins" />
<!-- VS install dir replaced by RegistrySearch -->
<Directory Id="VSINSTALLDIR" Name="VS2005" />
<!-- Components: files and registry entries -->
<Component Id="C_VsIdeTestHostAddinXml" Guid="$(var.C_VsIdeTestHostAddinXml_Guid)">
<Condition>VSIDETESTHOST_TEAMSUITE OR VSIDETESTHOST_TEAMDEV OR VSIDETESTHOST_TEAMTEST</Condition>
<File Id="VsIdeTestHostAddinFile" Name="HAAddin.ADD" Vital="yes" DiskId="1" LongName="$(var.AddinName).AddIn" Source="$(var.InstallerSources)\VsIdeTestHost.AddIn" />
<XmlFile Id="AddAssemblyElement" Action="setValue" File="[#VsIdeTestHostAddinFile]" ElementPath="/Extensibility/Addin/Assembly" Value="[#VSIdeAddinDllFile]" Sequence="1" />
<XmlFile Id="AddinNameElement" Action="setValue" File="[#VsIdeTestHostAddinFile]" ElementPath="/Extensibility/Addin/FriendlyName" Value="$(var.AddinName)" Sequence="1" />
BTW, notice that the sample uses the “CommonAppDataFolder” variable, which is localized on Windows, while Visual Studio has a bug hardcoding the English name (%ALLUSERSPROFILE%\Application Data\Microsoft\MSEnvShared\AddIns) so the sample doesn’t work on non-English machines, as explained in the article INFO: Default .AddIn file locations for Visual Studio add-ins.
One of the things that I provide in my add-ins is that modal windows are 1) resizable and 2) with memory (that is, remembering the last size used by the user). I am passionate about user interface usability and I hate modal windows that are not resizable in today’s big screens, such as the Tools, Options window of Visual Studio. I know this is so because of historical reasons (MFC stuff or similar), but after 8 years and five Visual Studio releases (including a new WPF-based shell) you are still forced to scroll, despite you may have a big display… some new stuff like the windows related to data sources are resizable (they are implemented in .NET where resizing is more easy). I also hate the windows that are resizable, but the programmer didn’t take the extra step to persist the size when the window is closed, so the user must resize it each time that the window is opened…
Anyway, I have special code for all that stuff that resizes and centers the window before is shown, and while doing some testing with VS 2010 I noticed that something was wrong when the VS 2010 IDE was maximized because the window(s) of my add-in didn’t show centered. Finally I isolated the bug and it happens that DTE.MainWindow.Width property of the new WPF-based VS 2010 shell can return a wrong result if you maximize the IDE window by hand.
Here it is the acknowledged bug:
DTE.MainWindow.Width or DTE.MainWindow.Height don’t return correct size when IDE maximized by hand
Interestingly, it works fine if you resize the main window by code (and not by hand), so tricky and subtle are the bugs introduced by the new WPF-based shell.
Since I didn’t have any hope of getting this kind of bugs fixed for the RTM, I was able to workaround the problem in the add-in.
I updated my MZ-Tools 6.0 add-in to support Visual Studio 2010 Release Candidate and finally today March 1 I have released it.
Microsoft finally fixed most of the bugs that I have been reporting in the last months in the WPF-based commandbars so add-ins can get loaded in the IDE without crashing and AFAIK I was able to workaround all the issues that were not fixed (and won’t be in the RTM).
FWIW, some technical details:
– Internally this MZ-Tools build is compiled (using VS 2008) against .NET Framework 2.0 / CLR 2.0 and not the .NET Framework 4.0 / CLR 4.0 that VS 2010 uses (and the only that ships), but it works fine AFAIK.
– Although this MZ-Tools 6.0 build ships several assemblies (one for each VS IDE) for historical reasons (it still supports VS.NET 2002/2003), I have done tests for future versions and it seems to be possible to build a single assembly in .NET Framework 2.0 / CLR 2.0 that targets VS 2005, 2008 and 2010, using references provided by VS 2005 (that are also available in next VS versions) or using Reflection for references whose version changes between IDE versions (I think that you can use assembly redirections too).