2016-03-04 99 views
0

我有一个助手类为共享内存中一个COM对象的引用我的单元测试:COM对象被释放非故意

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

几何是从一个存储XML文件反序列化作为资源文件并附加到项目中。此后,它被包裹成一个COM对象:

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

现在我有一个使用存储在这个类的几何两种测试的方法:

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

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

当运行第二个,我收到了收到COMException:

不能使用 已从与其基础RCW分开

COM对象

我想知道为什么COM对象的引用被释放,因为它在UnitTestGeometryProvider中标记为static,我没有明确释放它。因此,即使如果管理资源的实例将走出去的范围(这是不它是静态的),下面的COM对象应消失,只有当所有我的测试结束时,或者当应用程序终止比较一般,或者我错过了什么?

我正在使用ArcObjects和Visual NUnit。

+2

使用COM对象初始化* static *字段是一个非常糟糕的主意,只能偶然使用。您无法保证这发生在正确的时间和正确的线程上。重要的是*一个*,公寓线程的COM对象由一个特定的线程拥有,如果该线程结束,那么该对象就像门帽一样死了。如果对象仍然为空,请使用属性来调用Deserialize()。 –

+0

@HansPassant好吧,但不是上面的单线程测试给出的测试吗?这里没有涉及多线程,还是'static'暗示它自己? – HimBromBeere

+1

这样的变量从类型初始值设定项(又名静态构造函数)中获取它的值。 .NET对于何时或如何运行提供了很少的保证,只是它们足够早地运行。你有一个测试运行者,可能有自己的想法,它在运行测试之前如何初始化所有的东西。如果你想要保证这永远不会出错,那么你不能得到一个,你有非常有力的证据证明它*出错了。 –

回答

0

由于汉斯帕桑特的评论,我发现实际问题。

显然,视觉,NUnit的框架决定创建为每个测试一个单独的线程。因此,每当我创建一个COM对象时(无论它是否为静态的),该对象都驻留在这个单独的线程中,不能在另一个线程中使用。如果线程死亡也做COM对象或更精确的引用它。这会导致GC踢出抛出COM对象,因为不存在对该线程的更多托管引用

该解决方案是相当straitforward:我改变静磁场,以实例的成员和我的测试类内创建UnitTestGeometryProvider类型的实例部件。因此每个测试都会生成一个新的提供者。
但是这种解决方案是很烦人的,因为Geometry - 属性必须被初始化,并为此对每个测试的Deserialize - 方法运行,而不是只有一次的所有测试。

我不知道是否有一个线程安全解决方案时intialized它的第一个线程死亡不杀参考COM对象。

+0

我不确定它是否对您有用,但是我发现在处理COM和线程时使用'StaThreadSyncronizer'方便,请参阅[了解SynchronizationContext](http://www.codeproject.com/Articles/31971/) Understanding-SynchronizationContext-Part-I)和后续文章。这允许与COM相关的所有内容在同一个线程中执行。但我从未需要在单元测试中使用它。 – wimh