Sie müssen alle lokalen Objekte in dem Bereich, in dem Sie sie erstellen, manuell freigeben. Wenn Sie Office-Anwendungen über die Automatisierung verwenden, verlassen Sie sich nicht auf den Garbage Collector, um diese Objekte zu bereinigen. Selbst wenn dies richtig ist, kann es einige Zeit dauern, bis sie sich einschaltet und Sie möglicherweise temporäre Objekte mit Referenzen auf andere Objekte erhalten dass du denkst, dass sie schon weg sind.
This is a somewhat related question mit mehr Details, die möglicherweise für Sie gelten, wenn Sie versuchen, Excel von Ihrer Anwendung mit Excel zu starten, die ausgeblendet wird.
Der Teil, den Sie auf jeden Fall relevant ist, ist dies:
- Wrap jede einzelne Funktion, die Excel in einem
try..catch
Block verwendet eine mögliche Ausnahme zu erfassen.
- Geben Sie alle Excel-Objekte immer explizit frei, indem Sie
Marshal.ReleaseComObject()
aufrufen und dann Ihre Variablen auf null
setzen, sobald Sie sie nicht benötigen. Geben Sie diese Objekte immer in einem finally
-Block frei, um sicherzustellen, dass ein fehlgeschlagener Excel-Methodenaufruf nicht zu einem dangling COM-Objekt führt.
- Wenn ein Fehler auftritt, schließen Sie die von Ihnen verwendete Excel-Instanz. Es ist nicht wahrscheinlich, dass Sie von Excel-bezogenen Fehlern wiederherstellen können und je länger Sie die Instanz behalten, desto länger werden Ressourcen verbraucht.
- Wenn Sie Excel beenden, stellen Sie sicher, dass Sie diesen Code vor rekursiven Aufrufen schützen. Wenn Ihre Ausnahmehandler versuchen, Excel herunterzufahren, während Ihr Code bereits Excel herunterfährt, wird am Ende ein toter Excel angezeigt Beispiel.
- Rufen Sie
GC.Collect()
und GC.WaitForPendingFinalizers()
direkt nach Aufruf der Application.Quit()
-Methode auf, um sicherzustellen, dass das .NET Framework alle Excel-COM-Objekte sofort freigibt.
bearbeiten: das ist, nachdem Sie mehr Details zu Ihrer Frage hinzugefügt.
In otherComponent
Sie nicht über die Workbook
und Document
Objekte freizugeben müssen. Diese beiden Objekte werden in Ihrem ersten Objekt erstellt, was bedeutet, dass das erste Objekt der Eigentümer ist. Da es das erste Objekt ist, das Ihre Top-Level-Excel-Objekte besitzt (vorausgesetzt, Sie haben auch ein Application
Objekt irgendwo), kann Ihr erstes Objekt otherComponent
aufrufen, Workbook
und Document
übergeben und dann bei der Rückkehr, bereinigen sie.Wenn Sie keines dieser Objekte in Ihrer MainComponent
verwenden, sollten Sie die Excel-bezogenen Objekte innerhalb otherComponent
erstellen und sie dort aufräumen.
Mit COM-Interop sollten Sie Ihre COM-Objekte so nahe an dem Ort, wo Sie sie benötigen, erstellen und sie so schnell wie möglich freigeben. Dies gilt insbesondere für Office-Anwendungen.
Ich machte diese Klasse, um COM-Objekte einfacher zu machen: dieser Wrapper ist Einweg, so können Sie using(...)
mit Ihren COM-Objekten verwenden - wenn der Bereich using
vorbei ist, gibt der Wrapper das COM-Objekt.
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;
}
}
}
}
Im Allgemeinen sollte die Methode, die ein Objekt erstellt hat, für die Bereinigung zuständig sein. Es ist etwas vage, was in diesem Szenario aus "Aufräumen" besteht. Sie sollten den Initialisierungs- und Bereinigungscode sowie einige weitere Erklärungen zu dem, was Sie tun, "die später Probleme verursachen könnten", veröffentlichen. –