«Никогда не используйте две точки с COM-объектами» - это хорошее правило, позволяющее избежать утечки COM-ссылок, но Excel PIA может привести к утечке больше, чем кажется на первый взгляд.
Одним из таких способов является подписка на любое событие, представляемое любым из COM-объектов объектной модели Excel.
Например, подписка на событие WorkbookOpen класса Application.
Немного теории о событиях COM
Классы COM предоставляют группу событий через интерфейсы обратного вызова. Чтобы подписаться на события, клиентский код может просто зарегистрировать объект, реализующий интерфейс обратного вызова, и класс COM будет вызывать его методы в ответ на определенные события. Поскольку интерфейс обратного вызова является интерфейсом COM, обязанность реализующего объекта - уменьшить счетчик ссылок любого COM-объекта, который он получает (в качестве параметра) для любого из обработчиков событий.
Как Excel PIA представляет события COM
Excel PIA представляет события COM класса приложения Excel как обычные события .NET. Всякий раз, когда код клиента подписывается на событие .NET (акцент на 'a'), PIA создает экземпляр класса, реализующего интерфейс обратного вызова, и регистрирует его в Excel.
Следовательно, несколько объектов обратного вызова регистрируются в Excel в ответ на различные запросы на подписку из кода .NET. Один объект обратного вызова для каждой подписки на событие.
Интерфейс обратного вызова для обработки событий означает, что PIA должна подписываться на все события интерфейса для каждого запроса подписки на событие .NET. Он не может выбирать. При получении обратного вызова события объект обратного вызова проверяет, заинтересован ли связанный обработчик события .NET в текущем событии или нет, а затем либо вызывает обработчик, либо молча игнорирует обратный вызов.
Влияние на количество ссылок на экземпляры COM
Все эти объекты обратного вызова не уменьшают счетчик ссылок ни одного из COM-объектов, которые они получают (в качестве параметров) для любого из методов обратного вызова (даже для тех, которые игнорируются без предупреждения). Они используют исключительно сборщик мусора CLR для освобождения COM-объектов.
Поскольку запуск GC является недетерминированным, это может привести к задержке процесса Excel на более длительный срок, чем требуется, и создать впечатление «утечки памяти».
Решение
На данный момент единственным решением является избежать поставщика событий PIA для класса COM и написать свой собственный поставщик событий, который детерминистически высвобождает объекты COM.
Для класса Application это можно сделать, реализовав интерфейс AppEvents, а затем зарегистрировав реализацию в Excel, используя Интерфейс IConnectionPointContainer . Класс Application (и в этом отношении все COM-объекты, отображающие события с использованием механизма обратного вызова) реализует интерфейс IConnectionPointContainer.