Используя Интерфейсные объекты для надлежащей чистки Excel interop объекты

Все эти вопросы:

борьба с проблемой, что C# не выпускает COM-объекты Excel правильно после использования их. Существует главным образом два направления работы вокруг этой проблемы:

  1. Уничтожьте процесс Excel, когда Excel не будет больше использоваться.
  2. Всего хорошего, чтобы явно присвоить каждый COM-объект, привыкший к переменной сначала и гарантировать это в конечном счете, Маршала. ReleaseComObject выполняется на каждом.

Некоторые заявили, что 2 слишком утомительно и всегда существует некоторая неуверенность, забываете ли Вы придерживаться этого правила в некоторых местах в коде. Все еще 1 кажется грязным и подверженным ошибкам мне, также я предполагаю, что в ограниченной среде, пытающейся уничтожить процесс, мог повысить ошибку безопасности.

Таким образом, я думал о решении 2 путем создания другой объектной модели прокси, которая подражает объектной модели Excel (для меня, это было бы достаточно для реализации объектов, в которых я на самом деле нуждаюсь). Принцип посмотрел бы следующим образом:

  • Каждый класс Excel Interop имеет свой прокси, который переносит объект того класса.
  • Прокси выпускает COM-объект в своем финализаторе.
  • Прокси подражает интерфейсу класса Interop.
  • Любые методы, которые первоначально возвратили COM-объект, изменяются для возврата прокси вместо этого. Другие методы просто делегируют реализацию к внутреннему COM-объекту.

Пример:

public class Application
{
    private Microsoft.Office.Interop.Excel.Application innerApplication
        = new Microsoft.Office.Interop.Excel.Application innerApplication();

    ~Application()
    {
        Marshal.ReleaseCOMObject(innerApplication);
        innerApplication = null;
    }

    public Workbooks Workbooks
    {
        get { return new Workbooks(innerApplication.Workbooks); }
    }
}

public class Workbooks
{
    private Microsoft.Office.Interop.Excel.Workbooks innerWorkbooks;

    Workbooks(Microsoft.Office.Interop.Excel.Workbooks innerWorkbooks)
    {
        this.innerWorkbooks = innerWorkbooks;
    }

    ~Workbooks()
    {
        Marshal.ReleaseCOMObject(innerWorkbooks);
        innerWorkbooks = null;
    }
}

Мои вопросы Вам в особенности:

  • Кто находит это плохой идеей и почему?
  • Кто находит это идеей жадности? Если так, почему кто-либо не имеет, реализовывал/публиковал такую модель уже? Действительно ли это происходит только из-за усилия, или я пропускаю проблему уничтожения с той идеей?
  • Действительно ли невозможно/плохо/подвержено ошибкам сделать ReleaseCOMObject в финализаторе? (Я только видел предложения вставить его Расположение (), а не в финализаторе - почему?)
  • Если подход имеет смысл, какие-либо предложения улучшать его?

11
задан Community 23 May 2017 в 10:27
поделиться

4 ответа

Невозможно/плохо/опасно ли делать ReleaseCOMObject в деструкторе? (Я видел только предложения поместить его в Dispose(), а не в деструктор - почему?)

Рекомендуется не помещать код очистки в финализатор, потому что в отличие от деструктора в C++ он вызывается не детерминированно. Он может быть вызван вскоре после того, как объект выйдет из области видимости. Это может занять час. Он может быть вызван никогда. В общем, если вы хотите утилизировать неуправляемые объекты, вам следует использовать паттерн IDisposable, а не финализатор.

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

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

5
ответ дан 3 December 2019 в 10:43
поделиться

Мы используем класс LifetimeScope, описанный в журнале MSDN. Его использование правильно очищает объекты и отлично работает с нашим экспортом в Excel. Код можно скачать здесь, а также содержит статью из журнала:

http://lifetimescope.codeplex.com/SourceControl/changeset/changes/1266

2
ответ дан 3 December 2019 в 10:43
поделиться

Посмотрите мой проект MS Office для .NET. . Решена проблема с объектами-оболочками referencich и собственными объектами через встроенную возможность позднего связывания VB.NET.

1
ответ дан 3 December 2019 в 10:43
поделиться

Что бы я сделал:

class ScopedCleanup<T> : IDisposable where T : class
{
    readonly Action<T> cleanup;

    public ScopedCleanup(T o, Action<T> cleanup)
    {
        this.Object = o;
        this.cleanup = cleanup;
    }

    public T Object { get; private set; }

    #region IDisposable Members

    public void Dispose()
    {
        if (Object != null)
        {
            if(cleanup != null)
                cleanup(Object);
            Object = null;
            GC.SuppressFinalize(this);
        }
    }

    #endregion

    ~ScopedCleanup() { Dispose(); }
}

static ScopedCleanup<T> CleanupObject<T>(T o, Action<T> cleanup) where T : class
{
    return new ScopedCleanup<T>(o, cleanup);
}

static ScopedCleanup<ComType> CleanupComObject<ComType>(ComType comObject, Action<ComType> actionBeforeRelease) where ComType : class
{
    return
        CleanupObject(
            comObject,
            o =>
            {
                if(actionBeforeRelease != null)
                    actionBeforeRelease(o);
                Marshal.ReleaseComObject(o);
            }
        );
}

static ScopedCleanup<ComType> CleanupComObject<ComType>(ComType comObject) where ComType : class
{
    return CleanupComObject(comObject, null);
}

Случай использования. Обратите внимание на вызов Quit, который, по-видимому, необходим для завершения процесса:

using (var excel = CleanupComObject(new Excel.Application(), o => o.Quit()))
using (var workbooks = CleanupComObject(excel.Object.Workbooks))
    {
        ...
    }
0
ответ дан 3 December 2019 в 10:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: