MZ-Tools Articles Series updated

Many of the MZ-Tools Articles that I have written in the last years seemed a bit outdated because each one has a header like this:

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio .NET 2002
Date: March 2010   Microsoft Visual Studio .NET 2003

  Microsoft Visual Studio 2005

Where the date and the list of Visual Studio versions would reflect the moment in time when I wrote them.

I have reviewed all them (200+) and many are now updated to reflect that they apply until the last Visual Studio 2012 version when that is the case:

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio .NET 2002
Date: March 2010   Microsoft Visual Studio .NET 2003
Updated: March 2013   Microsoft Visual Studio 2005
      Microsoft Visual Studio 2008
      Microsoft Visual Studio 2010
      Microsoft Visual Studio 2012

So, most HOWTO articles are now current. The exceptions are those articles which are BUG or PRB (issue), that I haven’t bothered to check if they are still present.

The strange case of “LoaderLock was detected” with a COM add-in written in .NET

Since some days ago, I was getting the following error when closing Visual Basic 6.0 from the Visual Studio debugger (I am developing a .NET-based version of MZ-Tools for the 64-bit VBA editor of Office, and VB6 will get it too):

LoaderLock was detected
Message: Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.

This is a warning of the Managed Debugging Assistants (MDA) of Visual Studio.

Today I decided to investigate. Soon it was clear that it was caused by the test-runner add-in that I created to run integration tests within VB 6.0. Since the error was caused during shutdown, I removed initializations (and the corresponding cleanups) to isolate the problem and I discovered that the problem was in this method:

internal List<string> GetAddinProgIds()
{
   List<string> colAddinProgIds;

   colAddinProgIds = new List<string>();

   foreach (AddIn objAddIn in m_objVBE.Addins)
   {
      colAddinProgIds.Add(objAddIn.ProgId);
   }
   return colAddinProgIds;
}

That method gets the registered add-ins of the VBE object (to load them in a combobox and select an add-in to run its test suites).

I soon realized that maybe I should release properly some COM object and certainly the problem was fixed:

internal List<string> GetAddinProgIds()
{
   List<string> colAddinProgIds;
   Addins colAddins;

   colAddinProgIds = new List<string>();

   colAddins = m_objVBE.Addins;
   foreach (AddIn objAddIn in colAddins)
   {
      colAddinProgIds.Add(objAddIn.ProgId);
   }

   // The following statement is to prevent the following error:
   // LoaderLock was detected
   // Message: Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain
   // or image initialization function since doing so can cause the application to hang.
   System.Runtime.InteropServices.Marshal.ReleaseComObject(colAddins);

   colAddins = null;

   return colAddinProgIds;
}

MZ-Tools Articles Series: BUG: DTE.ActiveDocument.ActiveWindow does not return the active document window if toolwindow active

My first small article this year is to document this bug of Visual Studio 2005, 2008, 2010 and 2012:

BUG: DTE.ActiveDocument.ActiveWindow does not return the active document window if toolwindow active
http://www.mztools.com/articles/2013/MZ2013001.aspx

Which I have just reported to Microsoft Connect:

Document.ActiveWindow does not return always the active window
https://connect.microsoft.com/VisualStudio/feedback/details/781522/document-activewindow-does-not-return-always-the-active-window

and that is a variation of a bug that I reported for VS.NET 2002/2003 and that I thought it was definitely fixed in VS 2005 (the relevant factor is that it still fails if a toolwindow is active):

BUG: Document.ActiveWindow does not return always the active window in Visual Studio .NET 2002/2003
http://www.mztools.com/articles/2004/MZ2004013.aspx

The strange case of “Set property ‘System.Windows.ResourceDictionary.DeferrableContent’ threw an exception.”

In the recent days, each time that I clicked the New Project button of the Visual Studio 2012 IDE, I got this exception:

“Set property ‘System.Windows.ResourceDictionary.DeferrableContent’ threw an exception.”

I have been clueless about this problem until today. When a problem happens in Visual Studio, the recommended approach is to launch it in “Safe mode”, because maybe an extension (add-in, package, etc.) is causing it. As a previous step, what I did today is to take a look at the Add-In Manager and I noticed that I had an add-in (a test runner that I created to perform integration tests of my MZ-Tools add-in) marked to load on startup. I unmarked it and then the problem disappeared. Why was this add-in causing this problem?

After some isolation, it happened that this add-in was setting an event handler for the AppDomain.AssemblyResolve event (to locate required assemblies) and a silenced NullReferenceException was happening in the event handler. The following minimal add-in reproduces the issue:

public class Connect : IDTExtensibility2
{
   private DTE2 _applicationObject;
   private AddIn _addInInstance;
   private AppDomain _appDomain;

   public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
   {
      _applicationObject = (DTE2)application;
      _addInInstance = (AddIn)addInInst;

      switch (connectMode)
      {
         case ext_ConnectMode.ext_cm_Startup:
            // OnStartupComplete will be called
            break;

         case ext_ConnectMode.ext_cm_AfterStartup:
            InitializeAddIn();
            break;
       }
   }

   public void OnStartupComplete(ref Array custom)
   {
      InitializeAddIn();
   }

   private void InitializeAddIn()
   {
      _appDomain = AppDomain.CurrentDomain;
      _appDomain.AssemblyResolve += AppDomain_AssemblyResolve;
   }

   public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
   {
      _appDomain.AssemblyResolve -= AppDomain_AssemblyResolve;
   }

   public void OnAddInsUpdate(ref Array custom)
   {
   }

   public void OnBeginShutdown(ref Array custom)
   {
   }

   private Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args)
   {
      Assembly objAssembly = null;
      AssemblyName objAssemblyName = null;

      // Force a NullReferenceException
      if (objAssemblyName.Name == "")
      {
      }

      return objAssembly;
   }
}

The strange case of the add-in initialized twice

As I commented in my last post, I have developed my own integration test infrastructure to test my MZ-Tools add-in. As part of it, there is an add-in that is the test runner, that when loaded loads in turn the MZ-Tools add-in if not loaded, locates its friend assembly that contains the integration tests, loads it, gets the test suites, gets the test methods, shows them in a treeview and executes the ones that I select.

This worked fine if the test runner add-in was not marked to load on startup and I had to load it with the Add-in Manager. But when the test runner add-in was marked to load on startup, the MZ-Tools add-in was initialized twice, giving an exception because some code didn’t expect to run twice (duplicated key).

The code of the MZ-Tools initialization is similar to the one that I wrote in my article HOWTO: Use correctly the OnConnection method of a Visual Studio add-in and that I constantly recommend in the MSDN VSX Forums:

AddIn _objAddIn;
EnvDTE.DTE _objDTE;

void IDTExtensibility2.OnConnection(object objApplication, ext_ConnectMode eConnectMode, object objAddInInst, ref System.Array r_objCustom)
{
   _objDTE = (EnvDTE.DTE)objApplication;
   _objAddIn = (AddIn)objAddInInst;

   switch (eConnectMode)
   {
      case ext_ConnectMode.ext_cm_Startup:

         // IDTExtensibility2.OnStartupComplete will be called
         break;

      case ext_ConnectMode.ext_cm_AfterStartup:

         Initialize();
         break;
   }
}

void IDTExtensibility2.OnStartupComplete(ref System.Array r_objCustom)
{
   Initialize();
}

private void Initialize()
{
   ...
}

This pattern assumes that:

1) If an add-in is loaded on startup:

  • The OnConnection method will be called with the ext_ConnectMode.ext_cm_Startup flag.
  • The OnStartupComplete method will be called later TOO, when the Visual Studio IDE has completed its initialization.

2) If an add-in is loaded through the Add-In Manager:

  • The OnConnection method will be called with the
    ext_ConnectMode.ext_cm_AfterStartup flag.
  • The OnStartupComplete method will NOT be called (because the Visual Studio IDE was already initialized when you used the Add-In Manager to load the add-in).

But in my case, the Initialize method was being called twice. How was that?

It happens that there is a subtle behavior here: when an add-in marked to load on startup loads in turn another add-in (using EnvDTE.AddIn.Connect = true), in the second add-in the OnConnection method is called with the ext_ConnectMode.ext_cm_AfterStartup flag, AND the OnStartupComplete is called too!!! (because the Visual Studio IDE was not initialized when the first add-in was loaded on startup). So, the Initialize method is called twice.

I was about to report this as a bug, but I have thought that maybe the behavior is correct after all, that is, when VS has finished its initialization it calls OnStartupComplete for all add-ins that are loaded in that moment, independently of whether they were marked to load on startup or they were loaded by another add-in marked to load on startup. And what is really misleading is the MSDN documentation about the OnStartupComplete method:

(OnStartupComplete) “Occurs whenever an add-in, which is set to load when Visual Studio starts, loads.”

That implies that if add-in is not set to load on startup, its OnStartupComplete method will not be called.

The Remarks section is correct, though, since it does not relate the OnStartupComplete call to whether the add-in was set to load on startup or not:

“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. Using OnStartupComplete guarantees that the Visual Studio integrated development environment (IDE) has completed the startup process.”

As you realize, this is a subtlety that your add-in won’t experience unless is loaded by another add-in when Visual Studio is started.

Long time without blogging

I have been long time without blogging, and the reason is that I have been quite busy doing the following:

– I have migrated the whole code of my MZ-Tools 7.0 add-in from VB.NET to C#. After 10 years programming in VB.NET, I decided to switch to C# and the best way is to use it everyday, so I had to migrate the product. The migration was successful and build 7.0.0.103 released on December 1 was C#-based. Only a couple of bugs were introduced that were not detected by the automated integration tests. FWIW, I used Instant C# from Tangible Software Solutions.

– I have enforced the Code Analysis feature of Visual Studio on the MZ-Tools code base with All Microsoft Rules, and after lots of fixes I was able to pass all with some custom suppressions and four of them disabled: CA1031 Do not catch general exception types, CA1502 Avoid excessive complexity, CA1506 Avoid excessive class coupling and CA5122 P/Invoke declarations should not be safe-critical. If you have tried to enforce them on a large code base you know how time-consuming is that.

– I have done massive architectural changes in the code base of MZ-Tools for Visual Studio to prepare a new unified version 8.0, .NET-based, that will support Visual Studio (VB.NET, C#), Visual Basic 6.0 (“Classic”), VBA editor of Office 32-bit and VBA editor of Office 64-bit. That means to encapsulate the automation models of VS and VB “classic”. I already have the user interface, options, setup and unit-test/integration-test subsystems. It “only” remains the features, but it will take me months yet :-). I will blog about this in the next months when I am closer to the release.

– I have created an integration test runner that runs in the IDE where MZ-Tools is loaded, rather than in the IDE where the MZ-Tools source code project is loaded. While the Visual Studio SDK provides a remote MS-Test-based host adapter for this purpose, I tried it two years ago with disappointing results, so I created my own integration testing infrastructure in MZ-Tools. But the MZ-Tools add-in, its integration tests and the test-runner were in the same assembly (using a special configuration). Now I have isolated them so I have the add-in, the integration tests and the test-runner in three separate assemblies. I hope to release the test runner in CodePlex or similar some day.

– I migrated to Visual Studio 2012 (I am almost used to the new UI style) and I am planning to adopt TFS (I am finishing the simultaneous reading of Professional Team Foundation Server 2012 and Testing for Continuous Delivery with Visual Studio 2012). In the past I used Perforce, but after a failed restore after a crash (likely my fault) I didn’t use source control for some time and I want to adopt TFS now.

And I was almost three weeks on vacation during Christmas, resting and watching lots of TV series 🙂

10 years as Microsoft Most Valuable Professional (MVP)

Happy New Year!

I have received a new Most Valuable Professional (MVP) award from Microsoft just a couple of hours ago. This is a very special one for me because it is my 10th MVP award and, as always, I am very grateful to Microsoft for this recognition:

“Dear Carlos Quintero,

Congratulations! We are pleased to present you with the 2013 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others. We appreciate your outstanding contributions in Visual Basic technical communities during the past year.”

A wrong way of checking the Visual Studio version where an add-in is loaded

Take a look at this code of an add-in that checks that it is loaded only in Visual Studio 2012 and try to figure out what’s wrong:

private DTE2 _applicationObject;
private AddIn _addInInstance;

public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
   const string REGISTRY_KEY_VS_11_0 = "SOFTWARE\MICROSOFT\VISUALSTUDIO\11.0";

   string addInRegistryRoot = null;

   _applicationObject = (DTE2)application;
   _addInInstance = (AddIn)addInInst;

   switch (connectMode)
   {
      case ext_ConnectMode.ext_cm_AfterStartup:
      case ext_ConnectMode.ext_cm_Startup:

         addInRegistryRoot = _applicationObject.RegistryRoot.ToUpper();

         if (!addInRegistryRoot.StartsWith(REGISTRY_KEY_VS_11_0))
         {
            System.Windows.Forms.MessageBox.Show("This add-in only works with Visual Studio 2012");
         }
         break;
   }
}

If you have discovered it, either you are quite expert on globalization issues or you are a Turkish developer. My MZ-Tools add-in had similar code that has worked correctly for years until a few weeks ago, when a developer reported that using the Turkish culture (user interface, language input keyboard, etc.) MZ-Tools refused to load. You can simulate that effect inserting the following line at the start the OnConnection method that sets the Turkish culture for the current thread:

System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(“tr-TR”);

The reason is that in Turkish, the upper case of the character i is not I but İ (with a dot), as explained here. So _applicationObject.RegistryRoot.ToUpper() returns the following string:

“SOFTWARE\MİCROSOFT\VİSUALSTUDİO\11.0”

which is different from:

“SOFTWARE\MICROSOFT\VISUALSTUDIO\11.0”

Of course, the solution is to use _applicationObject.RegistryRoot.ToUpperInvariant() or an overloaded version of StartsWith() that uses the ordinal or invariant culture comparison ignoring the case.

So, if you make software for international markets, ensure that you run the Code Analysis feature of Visual Studio with the Microsoft Globalization Rules, because two rules are violated with the code above:

– CA1304: Specify CultureInfo (in _applicationObject.RegistryRoot.ToUpper())
– CA1307: Specify StringComparison (in sAddInRegistryRoot.StartsWith(REGISTRY_KEY_VS_11_0))

VS SDK, packages, add-ins, macros and more…