Как стать автором
Обновить

Комментарии 8

Для нашего ком прокси (который нечто отнаследованное от MarshalByRefObject), при приходе горбатого коллектора позовут Dispose()

wat?
(подмигивает) ну, формально там нет IDisposable, и я знаю что вы это знаете ;-) Но нечто похожее таки там есть. Честно скажу — я после таких сюрпризов просто побоялся посмотреть в ildasm — там Dispose(), Finalize(), или ~__ComObject(). Для гипотезы это несущественно, а перебить себе сон я был неготов ;-)
Ибо при написании ком сервера (который вызывается откуда угодно) ожидать что мастер-коллекции скаждут Release() и продолжат использовать child elements, часть из которых останется uninitialized… Странный паттерн.

однако КОМ-объекты виноваты. Это вполне нормальный случай использования, когда мастер коллекции говорят Release(), а детей продолжают использовать. Если при этом мастер-коллекция грохнула дочерние элементы не глядя на их счетчики — оборвать руки писателю мастер-коллекции.
Или я неправильно понял суть проблемы?
Есть некий COM от внешнего сервера, не InProc (то есть с маршалингом и автопроксями). У него есть проперть возвращающая коллекцию в смысле COM — в данном примере стандартный Count()/_Item() в наличии, судя по всему, раз по индексу работает.

Дотнет генерит оберток интеропом, то есть никаких динамик биндингов как бы нет.

В нагенереном дотнетом (не в коме!) есть стандартный GetEnumerator().

Хождение по этому дотнетовскому енумератору что foreach что while(!foo.MoveNext()) приводит к дисконнекту. То есть в процессе хождения / энумерации, собственно энумератор жив, а проперть по которой ходит (то есть вся коллекция) — уже нет. Те ком объекты которые я успеваю получить до COMException — - работают дальше.

Что смешно, точно такая же работа без GetEnumerator() то есть по индексу — к дисконнекту не приводит, так что очень вряд ли виноват сам ком объект.

Ну и совсем крышесносяще, если эту проперть просто присвоить локальной переменной — то снова все работает.
Действительно, проблема в COM. Я несколько лет разрабатывал COM-серверы на C++. Всякий разработчик COM знает, что енумератор, запрашиваемый у COM-коллекции через get__NewEnum, пока жив сам, должен держать ссылку на итерируемую коллекцию с поднятым счетчиком (за исключением копирующих енумераторов, которые копируют элементы коллекции в себя). В майкрософтовском ATL, на базе которого часто реализуются COM и коллекции в том числе, это сделано автоматом:
… holds a COM reference on the collection object to ensure that the collection remains alive while there are outstanding enumerators

Судя по симптомам, в Вашем COM-сервере об этом скорее всего забыли. Без исходников на 100% гарантировать конечно нельзя.

точно такая же работа без GetEnumerator() то есть по индексу — к дисконнекту не приводит

Как раз, потому что в этом варианте не участвует кривонаписанный COM-енумератор.
var asset = lib.Assets[i]; получаем коллекцию и напрямую (без промежуточного енумератора) из нее берем элемент.

Кстати, в COM можно реализовать проперти так, что Assets возвращает коллекцию, а Assets(i) сразу нужный элемент без создания и заполнения коллекции. Не знаю, поддерживает ли такое клиент на C#. Но это уже другая песня.
Вероятно вы правы.

Осталось только понять, почему

если эту проперть просто присвоить локальной переменной — то снова все работает.

;-)
Так очевидно же. Вы этой локальной переменной держите ссылку на коллекцию (то, что должен был делать правильно написанный енумератор) и не позволяете сборщику ее релизить раньше времени.
Следует дополнить, если кто-то вдруг не уловил:
.NET-овский енумератор в данном случае — это обертка над COM-енумератором.
foreach перед выполнением получает lib.Assets и запрашивает у него COM-енумератор через скрытый get__NewEnum и оборачивает в .NET-енуменатор, ссылка на саму коллекцию lib.Assets нигде в оригинальном коде не сохраняется. Енумератор живет до конца foreach, а ссылка на саму коллекцию пропадает сразу же перед входом в цикл. Если бы коллекция жила внутри lib, то все было бы ок, но, судя по всему (нужно убедиться в исходниках), проперти lib.Assets генерит новую коллекцию всякий раз, когда к нему обращаются.

P.S. COM-енумератор — полноценный COM-объект.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории