COM 对象被意外释放
COM-object was released unintendedly
我的单元测试有一个助手-class,它共享对内存中 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);
}
现在我有两个测试方法使用存储在这个 class 中的几何:
[TestClass()]
public class MyTest
{
[TestMethod()]
public void FirstTest()
{
var p = UnitTestGeometryProvider.Geometry;
}
[TestMethod()]
public void SecondTest()
{
var p = UnitTestGeometryProvider.Geometry;
}
}
当 运行 第二个我得到一个 COMException:
COM object that has been separated from its underlying RCW cannot be used
我想知道为什么要释放对 COM 对象的引用,因为它在 UnitTestGeometryProvider
中标记为 static
而我没有明确释放它。因此,即使 if 实例的 托管资源 也会超出范围(这不是静态的),底层 COM 对象应该仅当我的所有测试完成时或更一般的应用程序终止时才离开,或者我错过了什么?
我正在使用 ArcObjects 和 Visual NUnit。
由于 Hans Passant 的评论,我发现了实际问题。
显然,Visual-NUnit-Framework 决定为每个测试创建一个单独的线程。因此,每当我创建一个 COM 对象时——不管它是静态的还是非静态的——这个对象都存在于这个单一线程中,不能在另一个线程中使用。如果线程死了,COM 对象也会死掉,或者更准确地说,是对它的引用。这导致 GC 开始丢弃 COM 对象,因为在该线程中不再存在对其的托管引用。
解决方案非常直接:我将静态字段更改为实例成员,并在我的测试 class 中创建了一个类型为 UnitTestGeometryProvider
的实例成员。因此每次测试都会生成一个新的提供者。
然而,这个解决方案非常烦人,因为 Geometry
-属性 必须初始化,因此 Deserialize
-方法对每个测试运行,而不是对所有测试只运行一次。
我不知道是否有线程安全的解决方案可以在初始化它的第一个线程死亡时不终止对 COM 对象的引用。
我的单元测试有一个助手-class,它共享对内存中 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);
}
现在我有两个测试方法使用存储在这个 class 中的几何:
[TestClass()]
public class MyTest
{
[TestMethod()]
public void FirstTest()
{
var p = UnitTestGeometryProvider.Geometry;
}
[TestMethod()]
public void SecondTest()
{
var p = UnitTestGeometryProvider.Geometry;
}
}
当 运行 第二个我得到一个 COMException:
COM object that has been separated from its underlying RCW cannot be used
我想知道为什么要释放对 COM 对象的引用,因为它在 UnitTestGeometryProvider
中标记为 static
而我没有明确释放它。因此,即使 if 实例的 托管资源 也会超出范围(这不是静态的),底层 COM 对象应该仅当我的所有测试完成时或更一般的应用程序终止时才离开,或者我错过了什么?
我正在使用 ArcObjects 和 Visual NUnit。
由于 Hans Passant 的评论,我发现了实际问题。
显然,Visual-NUnit-Framework 决定为每个测试创建一个单独的线程。因此,每当我创建一个 COM 对象时——不管它是静态的还是非静态的——这个对象都存在于这个单一线程中,不能在另一个线程中使用。如果线程死了,COM 对象也会死掉,或者更准确地说,是对它的引用。这导致 GC 开始丢弃 COM 对象,因为在该线程中不再存在对其的托管引用。
解决方案非常直接:我将静态字段更改为实例成员,并在我的测试 class 中创建了一个类型为 UnitTestGeometryProvider
的实例成员。因此每次测试都会生成一个新的提供者。
然而,这个解决方案非常烦人,因为 Geometry
-属性 必须初始化,因此 Deserialize
-方法对每个测试运行,而不是对所有测试只运行一次。
我不知道是否有线程安全的解决方案可以在初始化它的第一个线程死亡时不终止对 COM 对象的引用。