На сегодняшний день, как правильно работать с COM-объектами?

Это очень распространенный вопрос, и я решил задать его, потому что на этот вопрос сегодня может быть другой ответ. Надеемся, ответы помогут понять, как правильно работать с COM-объектами. Лично я чувствую себя очень смущенным после получения различных мнений по этому вопросу.

Последние 5 лет я работал с COM-объектами, и для меня правила были очень ясны:

Используйте один период в строках кода. Используя более одного периода, создайте временные объекты за сценой, которые не могут быть явно освобождены.Не используйте foreach, используйте цикл for и освобождайте каждый элемент на каждой итерацииНе вызывайте FInalReleaseComObject, вместо этого используйте ReleaseComObject.Не используйте GC для освобождения объектов COM. GC намерение в основном для отладки использования.Выпускать объекты в обратном порядке их создания.

Некоторые из вас могут быть разочарованы после прочтения этих последних строк, это то, что я знал о том, как правильно создавать / выпускать Com Object, я надеюсь получить ответы, которые сделают его более понятным и неоспоримым.

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

Как правильно выпустить Excel COM объекты (Ноябрь 2013 г.)Правильный способ освобождения COM-объектов в .NET (Август 2011 г.)Marshal.ReleaseComObject считается опасным (Март 2010 г.)ReleaseCOMObject (Апрель 2004 г.)

«... В сценариях VSTO вам обычно не нужно использовать ReleaseCOMObject. ...»

MSDN -Метод Marshal.ReleaseComObject (текущая версия .NET Framework):

«... Вы должны использовать этот метод для освобождения базового COM-объекта, который содержит ссылки ...»

ОБНОВИТЬ:

Этот вопрос был помечен как слишком широкий. По запросу я постараюсь упростить и задать более простые вопросы.

Требуется ли ReleaseComObject при работе с COM-объектами или правильный способ вызова GC?Меняет ли подход VSTO способ, которым мы привыкли работать с COM-объектами?Какие из приведенных выше правил являются обязательными, а какие нет? Есть ли другие?
 adrianm20 июн. 2016 г., 10:37
Зачем вам вообще нужно вручную освобождать COM-объекты вместо того, чтобы полагаться на GC, чтобы в конечном итоге очистить? например содержат ли COM-объекты ограниченные ресурсы, такие как обработчики файлов и т. д.
 adrianm20 июн. 2016 г., 15:43
@SuperPeanut, что ты имеешь в виду? GC (рано или поздно) выпустит COM-объекты, которые, как мы надеемся, управляют своими собственными (незапланированными) ресурсами. Мой вопрос заключается в том, почему OP нужно выпускать COM-объекты напрямую, а не ждать GC? Я знаю, что есть причины для быстрого выпуска, но в общем случае я бы сказал: «Не делайте этого, если вы не находитесь в ситуации, когда поздний выпуск является проблемой».
 ehh19 июн. 2016 г., 09:40
@ElektroStudios, на самом деле FinalReleaseComObject выпустит все счетчики ссылок RCW сразу, но для лучшего управления и отладки я предпочел использовать ReleaseComObject. Использование FinalReleaseComObject может быстро привести к сбою вашей программы с сообщением «Com Object отделен от лежащего в его основе RCW».
 Martin Verjans20 июн. 2016 г., 11:31
@adrianm, потому что COM-объекты обычно являются неуправляемым кодом, а GC не может получить доступ к их ресурсам ...
 TnTinMn04 июл. 2016 г., 03:40
.Net V4.0 представилMarshal.AreComObjectsAvailableForCleanup, Я привел пример вэтот ответ.
 ElektroStudios19 июн. 2016 г., 09:23
COM - это COM, и на сегодняшний день он остается COM. Я не думаю, что ответственность за освобождение ресурса от разработчика. сторона и его использование и администрирование в .NET каким-то образом изменилось / эволюционировало, в любом случае я не гуру неуправляемого кода. Однако есть ли веская причина, по которой вы применяете 3-е правило все эти годы?FinalReleaseComObject должен освободить все счетчики ссылок RCW за один раз, избегая лишних одиночных вызововReleaseCOMObject, Смотрите этот ответ:stackoverflow.com/questions/1827059/...
 Govert04 июл. 2016 г., 14:09
@TnTinMn Это полезная находка - я добавил ваше предложение в код в своем ответе.

Ответы на вопрос(1)

Решение Вопроса

астности, сборщик мусора .NET правильно отслеживает ссылки COM и корректно освобождает объекты COM, если у них нет оставшихся ссылок времени выполнения. Вмешательство в подсчет ссылок COM-объекта путем вызоваMarshal.ReleaseComObject(...) или жеMarshal.FinalReleaseComObject(...) опасный, но распространенный анти-паттерн. К сожалению, некоторые плохие советы пришли от Microsoft.

Ваш код .NET может корректно взаимодействовать с COM, игнорируя все 5 ваших правил. Если вам нужно запустить детерминированную очистку COM-объектов, на которые больше нет ссылок из среды выполнения, вы можете безопасно принудительно запустить GC (и, возможно, дождаться завершения финализаторов). В противном случае вам не нужно делать ничего особенного в вашем коде для работы с COM-объектами.

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

В качестве примера приведем код C #, который взаимодействует с Excel и правильно очищается. Вы можете вставить в консольное приложение (просто добавьте ссылку на Microsoft.Office.Interop.Excel):

using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace TestCsCom
{
    class Program
    {
        static void Main(string[] args)
        {
            // NOTE: Don't call Excel objects in here... 
            //       Debugger would keep alive until end, preventing GC cleanup

            // Call a separate function that talks to Excel
            DoTheWork();

            // Now let the GC clean up (repeat, until no more)
            do
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            while (Marshal.AreComObjectsAvailableForCleanup());
        }

        static void DoTheWork()
        {
            Application app = new Application();
            Workbook book = app.Workbooks.Add();
            Worksheet worksheet = book.Worksheets["Sheet1"];
            app.Visible = true;
            for (int i = 1; i <= 10; i++) {
                worksheet.Cells.Range["A" + i].Value = "Hello";
            }
            book.Save();
            book.Close();
            app.Quit();

            // NOTE: No calls the Marshal.ReleaseComObject() are ever needed
        }
    }
}

Вы увидите, что процесс Excel правильно завершает работу, указывая, что все объекты COM были правильно очищены.

VSTO не меняет ни одну из этих проблем - это всего лишь библиотека .NET, которая упаковывает и расширяет собственную объектную модель COM Office.

Существует много ложной информации и путаницы по этому вопросу, в том числе много сообщений в MSDN и StackOverflow.

Что окончательно убедило меня поближе взглянуть и найти правильный совет, так это сообщениеhttps://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/ вместе с поиском проблемы со ссылками, оставшимися живыми в отладчике на ответ StackOverflow.

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

У меня нет никаких ссылок, чтобы указать, будет ли это нарушать договор COM. В целом, я ожидал бы, что иерархии COM будут использовать внутренние ссылки, чтобы гарантировать, что любые зависимости от последовательности должным образом управляются. Например. в случае с Excel можно ожидать, что объект Range сохранит внутреннюю ссылку на родительский объект Worksheet, поэтому пользователю объектной модели не нужно явно поддерживать оба объекта в действии.

Могут быть случаи, когда даже приложения Office чувствительны к последовательности, в которой высвобождаются объекты COM. Кажется, один случай, когда используется вложение OLE - см.https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/04/11/excel-ole-embedding-errors-if-you-have-managed-add-in-sinking-application-events-in-excel- 2 /

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

Но для общего взаимодействия с объектными моделями Office COM я согласен сVS сообщение в блоге вызов "Marshal.ReleaseComObject - проблема, замаскированная под решение".

 Govert03 июл. 2016 г., 19:00
@IvanStoev Мое терпение к этой теме уже истощилось.
 darul7511 июл. 2018 г., 23:16
Я пытался, что в прошлом это не работало, но на самом деле это работает довольно хорошо, спасибо Говерту
 ehh05 июл. 2016 г., 08:00
@SimonMourier, ваш комментарий помог мне понять мою путаницу. Когда я говорил, что последние 5 лет я использовал ReleaseComObject, это было, когда наша команда работала над очень большим проектом, и неуправляемый код был не Microsoft, а основным API, разработанным другой командой. Без освобождения COM-объектов у нас было много сбоев, и мы думали, что это способ правильно работать с COM-объектами. Помимо ответа Говерта, я готов принять рекомендованный способ работы с COM.
 Ivan Stoev03 июл. 2016 г., 18:33
10 звонков, чтобы доказать концепцию, серьезно? Давай, хотя бы попробуй с 10М :)
 Simon Mourier04 июл. 2016 г., 23:10
Я хотел бы добавить, что существует много COM-объектов, написанных в неуправляемом коде, которые имеют ошибки (не говоря о больших объектах Microsoft, которые в целом в порядке). Эти ошибки создают ложное представление о том, что P / Invoke не работает, поскольку иногда они заставляют разработчика .NET применять магию COM-паники вуду (везде добавлять ReleaseComObject, освобождать объекты в обратном порядке и т. Д.)
 Mathieu Guindon01 мая 2017 г., 16:25
Я знаю, что это довольно старо, но мне любопытно, насколько этот совет применим к коду взаимодействия COM, например, для (не?) Очистки закрепляемых окон инструментов в надстройке VBIDE, где размещен управляемый код .net с помощью приложения COM, в отличие от управляемого кода .netсоздание и уничтожение куча COM-объектов.

Ваш ответ на вопрос