c# - NullReferenceException in finalizer during MSTest -
(i know, ridiculously long question. tried separate question investigation far, it's easier read.)
i'm running unit tests using mstest.exe. occasionally, see test error:
on individual unit test method: "the agent process stopped while test running."
on entire test run:
1 of background threads threw exception: system.nullreferenceexception: object reference not set instance of object. @ system.runtime.interopservices.marshal.releasecomobject(object o) @ system.management.instrumentation.metadatainfo.dispose() @ system.management.instrumentation.metadatainfo.finalize()
so, here's think need do: need track downwards causing error in metadatainfo, i'm drawing blank. unit test suite takes on half hr run, , error doesn't happen every time, it's hard reproduce.
has else seen type of failure in running unit tests? able track downwards specific component?
edit:
the code under test mix of c#, c++/cli, , little bit of unmanaged c++ code. unmanaged c++ used c++/cli, never straight unit tests. unit tests c#.
the code under test running in standalone windows service, there's no complication asp.net or that. in code under test, there's threads starting & stopping, network communication, , file i/o local hard drive.
my investigation far:
i spent time digging around multiple versions of system.management assembly on windows 7 machine, , found metadatainfo class in system.management that's in windows directory. (the version that's under programme files\reference assemblies much smaller, , doesn't have metadatainfo class.)
using reflector inspect assembly, found seems obvious bug in metadatainfo.dispose():
// class system.management.instrumentation.metadatainfo: public void dispose() { if (this.importinterface == null) // <---- should "!=" { marshal.releasecomobject(this.importinterface); } this.importinterface = null; gc.suppressfinalize(this); }
with 'if' statement backwards, metadatainfo leak com object if present, or throw nullreferenceexception if not. i've reported on microsoft connect: https://connect.microsoft.com/visualstudio/feedback/details/779328/
using reflector, able find uses of metadatainfo class. (it's internal class, searching assembly should finish list.) there 1 place used:
public static guid getmvid(assembly assembly) { using (metadatainfo info = new metadatainfo(assembly)) { homecoming info.mvid; } }
since uses of metadatainfo beingness disposed, here's what's happening:
if metadatainfo.importinterface not null: static method getmvid returns metadatainfo.mvid the
using
calls metadatainfo.dispose dispose leaks com object dispose sets importinterface null dispose calls gc.suppressfinalize later, when gc collects metadatainfo, finalizer skipped. . if metadatainfo.importinterface null: static method getmvid gets nullreferenceexception calling metadatainfo.mvid. before exception propagates up,
using
calls metadatainfo.dispose dispose calls marshal.releasecomobject marshal.releasecomobject throws nullreferenceexception. because exception thrown, dispose doesn't phone call gc.suppressfinalize the exception propagates getmvid's caller. later, when gc collects metadatainfo, runs finalizer finalize calls dispose dispose calls marshal.releasecomobject marshal.releasecomobject throws nullreferenceexception, propagates way gc, , application terminated.
for it's worth, here's rest of relevant code metadatainfo:
public metadatainfo(string assemblyname) { guid riid = new guid(((guidattribute) attribute.getcustomattribute(typeof(imetadataimportinternalonly), typeof(guidattribute), false)).value); // above line retrieves 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 { { this.initnameandmvid(); homecoming this.mvid; } }
edit 2:
i able reproduce bug in metadatainfo class microsoft. however, reproduction different issue i'm seeing here.
reproduction: seek create metadatainfo object on file isn't managed assembly. throws exception constructor before
importinterface
initialized. my issue mstest: metadatainfo constructed on managed assembly, , something happens create
importinterface
null, or exit constructor before
importinterface
initialized. i know metadatainfo created on managed assembly, because metadatainfo internal class, , api calls passing result of assembly.location.
however, re-creating issue in visual studio meant downloaded source metadatainfo me. here's actual code, original developer's comments.
public void dispose() { // implement idisposable on class because imetadataimport // can expensive object maintain in memory. if(importinterface == null) marshal.releasecomobject(importinterface); importinterface = null; gc.suppressfinalize(this); } ~metadatainfo() { dispose(); }
the original code confirms seen in reflector: if statement backwards, , shouldn't accessing managed object finalizer.
i said before because never calling releasecomobject
, leaking com object. read more on utilize of com objects in .net, , if understand properly, incorrect: com object isn't released when dispose() called, released when garbage collector gets around collecting runtime callable wrapper, managed object. though it's wrapper unmanaged com object, rcw still managed object, , rule "don't access managed objects finalizer" should still apply.
try add together next code class definition:
bool _disposing = false // class property public void dispose() { if( !disposing ) marshal.releasecomobject(importinterface); importinterface = null; gc.suppressfinalize(this); disposing = true; }
c# .net mstest system.management