2016-03-04 2 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:

COM-объект, который был отделен от его основного RCW не может быть использован

Интересно, почему ссылка на COM-объект выпущена, поскольку она помечена static в UnitTestGeometryProvider, и я не могу ее явно освободить. Таким образом, даже , еслиуправляемый ресурс экземпляр выйдет за пределы области видимости (который не является статическим), базовый COM-объект должен уйти только тогда, когда все мои тесты закончены или более общие, когда приложение завершается, или мне что-то не хватает?

Я использую ArcObjects и Visual NUnit.

+2

Инициализация * статического поля с COM-объектом - довольно плохая идея и работает только случайно. У вас нет гарантии, что это произойдет в нужное время и в правильной нити. Что важно * многопользовательские, COM-объекты с резьбой, принадлежащие квартире, принадлежат определенному потоку, и если этот поток заканчивается, то объект мертв как дорнига. Вместо этого используйте свойство, вызывайте Deserialize(), если объект по-прежнему равен нулю. –

+0

@ HansPassant Ну, разве это тест, приведенный выше однопоточного теста? Здесь не задействована многопоточность, или же «статический» подразумевает ее собственный? – HimBromBeere

+1

Такая переменная получает свое значение от инициализатора типа (aka static constructor). .NET дает очень мало гарантий того, когда и как они работают, только для того, чтобы они работали достаточно рано. У вас есть тестовый бегун, у которого вполне может быть своя идея, как он инициализирует все, прежде чем запускает тест.Если вы хотите получить гарантию, что это никогда не пойдет не так, вы не сможете ее получить, у вас есть очень сильное доказательство того, что оно ошибочно. –

ответ

0

Из-за комментариев Ханса Пассана я нашел актуальную проблему.

Очевидно, что Visual-NUnit-Framework решает создать отдельный поток для каждого теста. Таким образом, всякий раз, когда я создаю COM-объект - будь он статическим или нет - этот объект живет в этом единственном потоке и не может использоваться в другом. Если нить также умирает, COM-объект или, точнее, ссылка на него. Это приводит к тому, что GC ударяет в выброс COM-объекта, поскольку в нем нет более управляемых ссылок на него в этом потоке.

Решение довольно просто: я изменил статическое поле на экземпляры-члены и создал экземпляр-член типа UnitTestGeometryProvider в моем тестовом классе. Таким образом, каждый поставщик генерирует новый провайдер.
Однако это решение довольно раздражает, потому что должно быть инициализировано свойство Geometry, и поэтому для каждого теста запускается только метод Deserialize, а не только один раз для всех тестов.

Я не знаю, есть ли потокобезопасное решение, чтобы не убивать ссылку на COM-объект, когда первый поток, который его инициализирует, умирает.

+0

Я не уверен, полезен ли он для вас, но я счел удобным использовать «StaThreadSyncronizer» при работе с COM и потоками, см. [Общие сведения о SynchronizationContext] (http://www.codeproject.com/Articles/31971/ Understanding-SynchronizationContext-Part-I) и последующих статей. Это позволяет использовать все, что связано с COM, в том же потоке. Но мне никогда не приходилось использовать это в модульном тесте. – wimh