0

I have a helper-class for my unit-tests that share a reference to an COM-object in memory:

public class UnitTestGeometryProvider 
{
    public static readonly IGeometry Geometry = Deserialize();
}

The geometry is deserialized from a Xml file which is stored as a resource file and appended to the project. Afterwards it is wrapped into a COM object:

public static IGeometry Deserialize() 
{
    return (IGeometry) new XMLSerializerClass().LoadFromString(myXDoc.OuterXml, null, null);
}

Now I have two test-methods that use the geometry stored within this class:

[TestClass()]
public class MyTest
{
    [TestMethod()]
    public void FirstTest()
    {
        var p = UnitTestGeometryProvider.Geometry;
    }   

    [TestMethod()]
    public void SecondTest()
    {
        var p = UnitTestGeometryProvider.Geometry;
    }
}

When running the second one I get a COMException:

COM object that has been separated from its underlying RCW cannot be used

I wonder why the reference to the COM-object is released as it is marked static in UnitTestGeometryProvider and I do not explicitly release it. So even if the managed resource to the instance would go out of scope (which is does not at it is static), the underlying COM object should go away only when all my tests finished or more general when the application terminates, or do I miss anything?

I am using ArcObjects and Visual NUnit.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • 2
    Initializing a *static* field with a COM object is a pretty bad idea and only works by accident. You have no guarantee that this happens at the right time and on the correct thread. Which matters a *lot*, apartment-threaded COM objects are owned by a specific thread and if that thread ends then the object is dead as a doornail. Use a property instead, call Deserialize() if the object is still null. – Hans Passant Mar 04 '16 at 15:06
  • @HansPassant Well, but isn´t this test given above a single-thread-test? No multi-threading is involved here, or does `static` imply its own? – MakePeaceGreatAgain Mar 07 '16 at 07:35
  • 1
    Such a variable gets its value from the type initializer (aka static constructor). .NET gives very few guarantees about when or how they run, only that they run early enough. You have a test runner that may well have its own idea how it initializes everything before it runs a test. If you want a warranty that this never goes wrong then you can't get one, you have very strong proof that it *does* go wrong. – Hans Passant Mar 07 '16 at 08:04

1 Answers1

0

Due to the comments by Hans Passant I found the actual problem.

Obviously the Visual-NUnit-Framework decides to create a separate thread for every test. Thus whenever I create a COM-object - be it static or not - this object lives on this single thread and cannot be used in another one. If the thread dies also does the COM-object or to be more precise the reference to it. This leads to the GC kicking in throwing the COM-object away as no more managed references to it exist within that thread.

The solution is pretty straitforward: I changed the static field to instance-members and created an instance-member of type UnitTestGeometryProvider within my test-class. Thus a new provider is generated by every test.
However this solution is quite annoying because the Geometry-property has to be initialized and therefor the Deserialize-method runs for every test instead of only once for all tests.

I don´t know if there is a thread-safe solution to not kill the reference to the COM-object when the first thread that intialized it dies.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • I'm not sure if it's useful for you, but I found it handy to use the `StaThreadSyncronizer` when dealing with COM and threads, see [Understanding SynchronizationContext](http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I) and the follow up articles. That allows everything which is COM related to be executed in the same thread. But I never needed to use that in a unit test. – wimh May 19 '16 at 08:52