title: NullReferenceException in finalizer during MSTest
body: <p>(I know, this is a ridiculously long question. I tried to separate the question from my investigation so far, so it's slightly easier to read.)</p>
  
  <p>I'm running my unit tests using MSTest.exe. Occasionally, I see this test error:</p>
  
  <p>On the individual unit test method: "The agent process was stopped while the test was running."</p>
  
  <p>On the entire test run:</p>
  
  <pre>
  One of the background threads threw exception: 
  System.NullReferenceException: Object reference not set to an instance of an object.
     at System.Runtime.InteropServices.Marshal.ReleaseComObject(Object o)
     at System.Management.Instrumentation.MetaDataInfo.Dispose()
     at System.Management.Instrumentation.MetaDataInfo.Finalize()
  </pre>
  
  <p>So, here's what I think I need to do: I need to track down what is causing the error in MetaDataInfo, but I'm drawing a blank. My unit test suite takes over half an hour to run, and the error doesn't happen every time, so it's hard to get it to reproduce.</p>
  
  <p>Has anyone else seen this type of failure in running unit tests? Were you able to track it down to a specific component?</p>
  
  <h3>Edit:</h3>
  
  <p>The code under test is a mix of C#, C++/CLI, and a little bit of unmanaged C++ code. The unmanaged C++ is used only from the C++/CLI, never directly from the unit tests. The unit tests are all C#. </p>
  
  <p>The code under test will be running in a standalone Windows Service, so there's no complication from ASP.net or anything like that. In the code under test, there's threads starting &amp; stopping, network communication, and file I/O to the local hard drive.</p>
  
  <hr>
  
  <h3>My investigation so far:</h3>
  
  <p>I spent some time digging around the multiple versions of the System.Management assembly on my Windows 7 machine, and I found the MetaDataInfo class in System.Management that's in my Windows directory. (The version that's under Program Files\Reference Assemblies is much smaller, and doesn't have the MetaDataInfo class.)</p>
  
  <p>Using Reflector to inspect this assembly, I found what seems to be an obvious bug in MetaDataInfo.Dispose(): </p>
  
  <pre><code>// From class System.Management.Instrumentation.MetaDataInfo:
  public void Dispose()
  {
      if (this.importInterface == null) // &lt;---- Should be "!="
      {
          Marshal.ReleaseComObject(this.importInterface);
      }
      this.importInterface = null;
      GC.SuppressFinalize(this);
  }
  </code></pre>
  
  <p>With this 'if' statement backwards, MetaDataInfo will leak the COM object if present, or throw a NullReferenceException if not. I've reported this on Microsoft Connect: <a href="https://connect.microsoft.com/VisualStudio/feedback/details/779328/" rel="nofollow noreferrer">https://connect.microsoft.com/VisualStudio/feedback/details/779328/</a></p>
  
  <p>Using reflector, I was able to find all uses of the MetaDataInfo class. (It's an internal class, so just searching the assembly should be a complete list.) There is only one place it is used:</p>
  
  <pre><code>public static Guid GetMvid(Assembly assembly)
  {
      using (MetaDataInfo info = new MetaDataInfo(assembly))
      {
          return info.Mvid;
      }
  }
  </code></pre>
  
  <p>Since all uses of MetaDataInfo are being properly Disposed, here's what's happening:</p>
  
  <ul>
  <li>If MetaDataInfo.importInterface is not null:
  
  <ul>
  <li>static method GetMvid returns MetaDataInfo.Mvid</li>
  <li>The <code>using</code> calls MetaDataInfo.Dispose
  
  <ul>
  <li>Dispose leaks the COM object</li>
  <li>Dispose sets importInterface to null</li>
  <li>Dispose calls GC.SuppressFinalize</li>
  </ul></li>
  <li>Later, when the GC collects the MetaDataInfo, the finalizer is skipped.</li>
  </ul></li>
  <li>.</li>
  <li>If MetaDataInfo.importInterface is null:
  
  <ul>
  <li>static method GetMvid gets a NullReferenceException calling MetaDataInfo.Mvid.</li>
  <li>Before the exception propagates up, the <code>using</code> calls MetaDataInfo.Dispose
  
  <ul>
  <li>Dispose calls Marshal.ReleaseComObject
  
  <ul>
  <li>Marshal.ReleaseComObject throws a NullReferenceException.</li>
  </ul></li>
  <li>Because an exception is thrown, Dispose doesn't call GC.SuppressFinalize</li>
  </ul></li>
  <li>The exception propagates up to GetMvid's caller.</li>
  <li>Later, when the GC collects the MetaDataInfo, it runs the Finalizer
  
  <ul>
  <li>Finalize calls Dispose
  
  <ul>
  <li>Dispose calls Marshal.ReleaseComObject
  
  <ul>
  <li>Marshal.ReleaseComObject throws a NullReferenceException, which propagates all the way up to the GC, and the application is terminated.</li>
  </ul></li>
  </ul></li>
  </ul></li>
  </ul></li>
  </ul>
  
  <p>For what it's worth, here's the rest of the relevant code from MetaDataInfo:</p>
  
  <pre><code>public MetaDataInfo(string assemblyName)
  {
      Guid riid = new Guid(((GuidAttribute) Attribute.GetCustomAttribute(typeof(IMetaDataImportInternalOnly), typeof(GuidAttribute), false)).Value);
      // The above line retrieves this Guid: "7DAC8207-D3AE-4c75-9B67-92801A497D44"
      IMetaDataDispenser o = (IMetaDataDispenser) new CorMetaDataDispenser();
      this.importInterface = (IMetaDataImportInternalOnly) o.OpenScope(assemblyName, 0, ref riid);
      Marshal.ReleaseComObject(o);
  }
  
  private void InitNameAndMvid()
  {
      if (this.name == null)
      {
          uint num;
          StringBuilder szName = new StringBuilder {
              Capacity = 0
          };
          this.importInterface.GetScopeProps(szName, (uint) szName.Capacity, out num, out this.mvid);
          szName.Capacity = (int) num;
          this.importInterface.GetScopeProps(szName, (uint) szName.Capacity, out num, out this.mvid);
          this.name = szName.ToString();
      }
  }
  
  public Guid Mvid
  {
      get
      {
          this.InitNameAndMvid();
          return this.mvid;
      }
  }
  </code></pre>
  
  <hr>
  
  <h3>Edit 2:</h3>
  
  <p>I was able to reproduce the bug in the MetaDataInfo class for Microsoft. However, my reproduction is slightly different from the issue I'm seeing here. </p>
  
  <ul>
  <li>Reproduction: I try to create a MetaDataInfo object on a file that isn't a managed assembly. This throws an exception from the constructor before <code>importInterface</code> is initialized. </li>
  <li>My issue with MSTest: MetaDataInfo is constructed on some managed assembly, and <em>something</em> happens to make <code>importInterface</code> null, or to exit the constructor before <code>importInterface</code> is initialized.
  
  <ul>
  <li>I know that MetaDataInfo is created on a managed assembly, because MetaDataInfo is an internal class, and the only API that calls it does so by passing the result of <a href="http://msdn.microsoft.com/en-us/library/system.reflection.assembly.location.aspx" rel="nofollow noreferrer">Assembly.Location</a>. </li>
  </ul></li>
  </ul>
  
  <p>However, re-creating the issue in Visual Studio meant that it downloaded the source to MetaDataInfo for me. Here's the actual code, with the original developer's comments.</p>
  
  <pre><code>public void Dispose()
  { 
      // We implement IDisposable on this class because the IMetaDataImport
      // can be an expensive object to keep in memory. 
      if(importInterface == null) 
          Marshal.ReleaseComObject(importInterface);
      importInterface = null; 
      GC.SuppressFinalize(this);
  }
  
  ~MetaDataInfo() 
  {
      Dispose(); 
  } 
  </code></pre>
  
  <p>The original code confirms what was seen in reflector: The if statement is backwards, and they shouldn't be accessing the managed object from the Finalizer. </p>
  
  <p>I said before that because it was never calling <code>ReleaseComObject</code>, that it was leaking the COM object. I read up more on the use of COM objects in .Net, and if I understand it properly, that was incorrect: The COM object isn't released when Dispose() is called, but it is released when the garbage collector gets around to collecting the Runtime Callable Wrapper, which is a managed object. Even though it's a wrapper for an unmanaged COM object, the RCW is still a managed object, and the rule about "don't access managed objects from the finalizer" should still apply.</p>