Отпустите объект Excel в моем деструкторе - PullRequest
8 голосов
/ 18 января 2010

Я пишу класс Excel, используя Microsoft.Interropt.Excel DLL. Я завершаю все функции, но у меня есть ошибка в моем деструкторе. Я хочу сохранить все изменения в моем файле, и я хочу выпустить все исходные тексты. Я хочу, чтобы все они были в моем деструкторе. Но в моем деструкторе объекты Excel.ApplicationClass, Workbook и Worksheet заполняются исключением, в котором есть сообщение «COM-объект, который был отделен от базового RCW, использовать нельзя». Поэтому я ничего не могу сохранить, ничего не закрыть, потому что я не могу получить доступ к книге или объекту листа.

Не могу ли я получить доступ к закрытым членам класса в Destructor?

Ответы [ 3 ]

16 голосов
/ 19 января 2010

Самое близкое, что есть в .NET к деструктору, это то, что .NET называет финализатором. Основное отличие состоит в том, что деструктор обычно имеет детерминированную финализацию (скажем, когда счетчик ссылок на объект становится равным нулю), а финализатор .NET вызывается через неопределенное время после того, как на объект больше не ссылаются. Это обрабатывается сборщиком мусора .NET с использованием процедуры трассировки корней, а не с помощью простого подсчета ссылок.

Одна из лучших статей на эту тему - Сборка мусора: автоматическое управление памятью в Microsoft .NET Framework . Подробнее о финализаторах см. Статью Финализация методов и деструкторов в MSDN.

Разве я не могу получить доступ к закрытому классу? члены в деструкторе?

Нет, вы не можете сделать это безопасно.

В вашем случае происходит то, что, когда корень больше не имеет прямой или косвенной ссылки на ваш объект, COM-объекты, на которые ссылается ваш объект, то есть объекты, на которые ссылаются ваши личные поля, также не упоминаются корнем либо. (Ссылка на поля вашего объекта не поддерживает эти COM-объекты живыми, потому что на ваш объект больше не ссылается или не отслеживает корень, и, следовательно, COM-объекты также не отслеживаются от корня .) Таким образом, ваш объект и все COM-объекты, на которые он ссылается, готовы к сборке мусора одновременно . Некоторое время спустя сборщик мусора очистит ваш объект и вызовет его финализатор, как это будет происходить и с COM-объектами, каждый из которых действительно является Runtime Callable Wrapper (RCW) .

Проблема в том, что не только время, когда эти объекты подлежат сборке мусора, является неопределенным, но и порядок , в котором вызываются финализаторы, также недетерминирован. В этом случае Runtime Callable Wrapper также имеет финализатор, который сам вызывает Marshal.ReleaseComObject , что приводит к уменьшению счетчика ссылок на стороне COM ограждения, так что этот объект COM может быть вышел. Но так как порядок вызова финализаторов неясен, вполне возможно, что финализаторы для COM-объектов, на которые ссылаются ваши объекты, будут запускать до финализатора для вашего объекта. Таким образом, код в вашем финализаторе может иногда работать , но, в большинстве случаев, одному или нескольким из вызываемых оболочек среды выполнения, для которых ссылки на ваши объекты уже будут вызывать свои финализаторы, и базовый COM-объект будет иметь выпущен до того, как ваш финализатор выполнит свой код.

Короче говоря, вам следует избегать использования финализаторов вообще, и вы никогда не должны обращаться к ссылочным типам из финализатора, потому что эти ссылочные типы, возможно, уже были завершены.

Чтобы исправить вашу ситуацию, я бы рассмотрел две разные возможности:

  1. Утилизируйте COM-объекты в том же методе, который их создает. У меня есть пара дискуссий по этому вопросу здесь и здесь .

  2. Включите детерминированное удаление вашего объекта, используя IDisposable интерфейс , вместо того, чтобы полагаться на недетерминированный финализатор.

Статьи о том, как реализовать шаблон IDisposable, см .:

- Майк

1 голос
/ 03 февраля 2010

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

В моем сценарии пользователь может закрыть книгу, прежде чем закрыть приложение. Я объявил объект Excel WithEvents и кодировал обработчик WorkbookBeforeClose для удовлетворения требований.

В этом сценарии я получаю ошибку «COM-объект, который был отделен от лежащего в его основе RCW, не может использоваться», когда я закрываю свое приложение (и я уже закрыл Excel). Ошибка происходит в Finalize, когда он вызывает Dispose (False).

Проблема исчезнет, ​​если я оставлю объект Excel, объявленный с событиями, но не закодирую никаких обработчиков.

В моем распоряжении я должен был проглотить ошибку для моих рабочих книг. Закрыть и выйти, так как они были утверждениями, вызвавшими ошибку.

0 голосов
/ 18 января 2010

Нет, вы не должны обращаться к каким-либо управляемым объектам в деструкторе: это включает в себя COM RCW.

Вместо этого реализуйте стандартный шаблон IDisposable и освободите ваши COM-объекты в методе Dispose (bool), как если бы вы использовали одноразовый управляемый объект.

...