Все эти вопросы:
борьба с проблемой, что C# не выпускает COM-объекты Excel правильно после использования их. Существует главным образом два направления работы вокруг этой проблемы:
Некоторые заявили, что 2 слишком утомительно и всегда существует некоторая неуверенность, забываете ли Вы придерживаться этого правила в некоторых местах в коде. Все еще 1 кажется грязным и подверженным ошибкам мне, также я предполагаю, что в ограниченной среде, пытающейся уничтожить процесс, мог повысить ошибку безопасности.
Таким образом, я думал о решении 2 путем создания другой объектной модели прокси, которая подражает объектной модели Excel (для меня, это было бы достаточно для реализации объектов, в которых я на самом деле нуждаюсь). Принцип посмотрел бы следующим образом:
Пример:
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 в деструкторе? (Я видел только предложения поместить его в Dispose(), а не в деструктор - почему?)
Рекомендуется не помещать код очистки в финализатор, потому что в отличие от деструктора в C++ он вызывается не детерминированно. Он может быть вызван вскоре после того, как объект выйдет из области видимости. Это может занять час. Он может быть вызван никогда. В общем, если вы хотите утилизировать неуправляемые объекты, вам следует использовать паттерн IDisposable, а не финализатор.
Это решение, на которое вы ссылались, пытается обойти эту проблему, явно вызывая сборщик мусора и ожидая завершения работы финализаторов. В целом это не рекомендуется, но в данной конкретной ситуации некоторые считают это приемлемым решением из-за сложности отслеживания всех временных неуправляемых объектов, которые создаются. Однако явная очистка - это правильный способ сделать это. Однако, учитывая сложность этого, такой "хак" может быть приемлемым. Обратите внимание, что это решение, вероятно, лучше, чем предложенная вами идея.
Если вместо этого вы хотите попробовать явную очистку, то руководство "не используйте две точки с COM-объектами" поможет вам не забыть сохранить ссылку на каждый создаваемый объект, чтобы вы могли очистить его, когда закончите.
Мы используем класс LifetimeScope, описанный в журнале MSDN. Его использование правильно очищает объекты и отлично работает с нашим экспортом в Excel. Код можно скачать здесь, а также содержит статью из журнала:
http://lifetimescope.codeplex.com/SourceControl/changeset/changes/1266
Посмотрите мой проект MS Office для .NET. . Решена проблема с объектами-оболочками referencich и собственными объектами через встроенную возможность позднего связывания VB.NET.
Что бы я сделал:
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))
{
...
}