2016-03-04 103 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