COM对象excel互操作清理

假设我有一个组件正在使用Workbook对象,并且在该方法体的中间某处我调用了另一个类的某个方法。 例如:

public class MainComponent { public void MyMainMethod() { OtherComponent otherComponent = new OtherComponent(); Workbook document; // some work with workbook object // working with document and worksheet objects. otherComponent.MethodCall(document); // some work with workbook object and it's worksheets. foreach(Worksheet sheet in document.Workheets) // do something with sheet } } public class OtherComponent { public void MethodCall(Workbook document) { string worksheetNames = ""; foreach(Worksheet sheet in document.Worksheets) worksheetNames += sheet.Name; Console.WriteLine(worksheetNames); } } 

并在那个otherComponent.MethodCall(文件); 我正在使用文档,我正在迭代它的工作表。

编辑更具体的问题。 我应该在文档和otherComponent.MethodCall(document)中的Worksheets上调用ReleaseCOMObject吗?

我从来没有真正解释过如何管理这个非托管代码。 如果有人能向我解释,我真的很感激。

您必须在创建它们的范围内手动释放所有本地对象。 通过自动化使用Office应用程序时,不要依赖垃圾收集器来清理这些对象 – 即使它正确,它可能需要一段时间才能启动,并且最终可能会出现持有对其他对象的引用的临时对象你认为已经消失了。

这是一个有点相关的问题 ,如果您尝试从隐藏Excel的应用程序中运行Excel,则可能会有更多详细信息。

与您完全相关的部分是:

  • try..catch块中包含使用Excel的每个函数来捕获任何可能的exception。
  • 始终通过调用Marshal.ReleaseComObject()显式释放所有Excel对象,然后在不需要时将变量设置为null 。 始终在finally块中释放这些对象,以确保失败的Excel方法调用不会导致悬空的COM对象。
  • 发生错误时,请关闭您正在使用的Excel实例。 您不可能从Excel相关的错误中恢复,并且保留实例的时间越长,它使用资源的时间就越长。
  • 当您退出Excel时,请确保保护该代码免受递归调用 – 如果您的exception处理程序在您的代码已经在关闭Excel的过程中尝试关闭Excel时,您将最终得到一个死Excel实例。
  • 在调用Application.Quit()方法之后立即调用GC.Collect()GC.WaitForPendingFinalizers()以确保.NET Framework立即释放所有Excel COM对象。

编辑这是在您向问题添加更多详细信息之后。

otherComponent您不需要释放WorkbookDocument对象。 在您的第一个对象中创建这两个对象,这意味着第一个对象是所有者。 由于它是拥有顶级Excel对象的第一个对象(假设您在某处还有一个Application对象),您的第一个对象可以调用otherComponent ,传入WorkbookDocument ,然后在返回时清理它们。 如果您从未在MainComponent使用任何这些对象, MainComponentMainComponent创建与Excel相关的对象并在otherComponent进行清理。

使用COM interop,您应该将COM对象创建为靠近您需要它们的位置,并尽快明确地释放它们。 对于Office应用程序尤其如此。

我使这个类更容易使用COM对象:这个包装器是一次性的,所以你可以使用你的COM对象using(...) – 当using范围结束时,包装器释放COM对象。

 using System; using System.Runtime.InteropServices; namespace COMHelper { ///  /// Disposable wrapper for COM interface pointers. ///  /// COM interface type to wrap. public class ComPtr : IDisposable { private object m_oObject; private bool m_bDisposeDone = false; ///  /// Constructor ///  ///  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; } ///  /// Constructor ///  ///  public ComPtr ( object oObject ) : this ( (T) oObject ) { } ///  /// Destructor ///  ~ComPtr () { Dispose ( false ); } ///  /// Returns the wrapped object. ///  public T Object { get { return ( (T) m_oObject ); } } ///  /// Implicit cast to type T. ///  /// Object to cast. /// Returns the ComPtr object cast to type T. public static implicit operator T ( ComPtr oObject ) { return ( oObject.Object ); } ///  /// Frees up resources. ///  public void Dispose () { Dispose ( true ); GC.SuppressFinalize ( this ); } ///  /// Frees up resurces used by the object. ///  /// When false, the function is called from the destructor. protected void Dispose ( bool bDispose ) { try { if ( !m_bDisposeDone && ( m_oObject != null ) ) { Marshal.ReleaseComObject ( m_oObject ); m_oObject = null; } } finally { m_bDisposeDone = true; } } } }