Вам придется выпустить все локальные объекты вручную в области, где вы их создаете. При использовании приложений Office через Automation не полагайтесь на сборщик мусора, чтобы очистить эти объекты - даже если он будет прав, может потребоваться время для его ввода, и вы можете оказаться в конечном итоге с временными объектами, содержащими ссылки на другие объекты что вы думаете, что уже ушли.
This is a somewhat related question с более подробной информацией, которая может быть применима к вам, если вы попытаетесь запустить Excel из приложения с скрытым Excel.
Та часть, которая, безусловно, отношение к вам это:
- Wrap каждая функция, которая использует Excel в
try..catch
блоке, чтобы захватить любое возможное исключение.
- Всегда явным образом освобождаю все объекты Excel, вызывая
Marshal.ReleaseComObject()
, а затем устанавливая переменные в null
, как только они вам не понадобятся. Всегда отпустите эти объекты в блоке finally
, чтобы убедиться, что неудачный вызов метода Excel не приведет к зависанию COM-объекта.
- При возникновении ошибки закройте экземпляр Excel, который вы используете. Вероятно, вы можете восстановить ошибки, связанные с Excel, и чем дольше вы держите экземпляр, тем дольше он использует ресурсы.
- Когда вы завершаете Excel, убедитесь, что вы защищаете этот код от рекурсивных вызовов - если обработчики исключений пытаются отключить Excel, пока ваш код уже находится в процессе выключения Excel, вы получите мертвый Excel пример.
- Вызвать
GC.Collect()
и GC.WaitForPendingFinalizers()
сразу после вызова метода Application.Quit()
, чтобы убедиться, что .NET Framework немедленно освобождает все объекты COM COM.
Edit: это после того, как вы добавили больше деталей на ваш вопрос.
В otherComponent
вам не нужно выпускать объекты Workbook
и Document
. Эти два объекта создаются в вашем первом объекте, что означает, что первым объектом является владелец. Поскольку это первый объект, который владеет вашими объектами Excel верхнего уровня (при условии, что у вас также есть объект Application
), ваш первый объект может позвонить otherComponent
, передать Workbook
и Document
, а затем при возврате очистить их.Если вы никогда не используете какой-либо из этих объектов в своем MainComponent
, тогда вы должны создать объекты, связанные с Excel, внутри otherComponent
и очистить их там.
С COM-взаимодействием вы должны создать свои COM-объекты как можно ближе к месту, где они вам нужны, и освободить их в явном виде, как только сможете. Это особенно актуально для приложений Office.
Я сделал этот класс, чтобы упростить использование COM-объектов: эта оболочка является одноразовой, поэтому вы можете использовать using(...)
с вашими COM-объектами - когда область using
закончена, оболочка освобождает COM-объект.
using System;
using System.Runtime.InteropServices;
namespace COMHelper
{
/// <summary>
/// Disposable wrapper for COM interface pointers.
/// </summary>
/// <typeparam name="T">COM interface type to wrap.</typeparam>
public class ComPtr<T> : IDisposable
{
private object m_oObject;
private bool m_bDisposeDone = false;
/// <summary>
/// Constructor
/// </summary>
/// <param name="oObject"></param>
public ComPtr (T oObject)
{
if (oObject == null)
throw (new ArgumentNullException ("Invalid reference for ComPtr (cannot be null)"));
if (!(Marshal.IsComObject (oObject)))
throw (new ArgumentException ("Invalid type for ComPtr (must be a COM interface pointer)"));
m_oObject = oObject;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="oObject"></param>
public ComPtr (object oObject) : this ((T) oObject)
{
}
/// <summary>
/// Destructor
/// </summary>
~ComPtr()
{
Dispose (false);
}
/// <summary>
/// Returns the wrapped object.
/// </summary>
public T Object
{
get
{
return ((T) m_oObject);
}
}
/// <summary>
/// Implicit cast to type T.
/// </summary>
/// <param name="oObject">Object to cast.</param>
/// <returns>Returns the ComPtr object cast to type T.</returns>
public static implicit operator T (ComPtr<T> oObject)
{
return (oObject.Object);
}
/// <summary>
/// Frees up resources.
/// </summary>
public void Dispose()
{
Dispose (true);
GC.SuppressFinalize (this);
}
/// <summary>
/// Frees up resurces used by the object.
/// </summary>
/// <param name="bDispose">When false, the function is called from the destructor.</param>
protected void Dispose (bool bDispose)
{
try
{
if (!m_bDisposeDone && (m_oObject != null))
{
Marshal.ReleaseComObject (m_oObject);
m_oObject = null;
}
}
finally
{
m_bDisposeDone = true;
}
}
}
}
В общем, метод, который создал объект, должен отвечать за очистку. Это несколько расплывчато, что состоит из «очистки» в этом сценарии. Вы должны опубликовать код инициализации и очистки, а также некоторое объяснение того, что вы делаете, что «может вызвать проблемы позже». –