2012-03-24 133 views
0

可以说我有一个组件正在做一些Workbook对象,并且在那个方法体的中间某处我调用了另一个类的某个方法。 例如:COM对象excel互操作清理

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(文件);我正在使用文档,并且正在遍历它的工作表。

编辑更具体的问题。我是否应该在文档和其他Component.MethodCall(文档)中的Worksheet上调用ReleaseCOMObject?

我从来没有真正有什么好的解释,应该如何管理这个非托管代码。 我真的很感激,如果有人可以向我解释这一点。

+0

通常,创建对象的方法应负责清理。在这种情况下,包含“清理”的内容有些模糊。你应该发布初始化和清理代码,以及更多关于你在做什么的解释,“以后可能会导致问题”。 – 2012-03-24 13:24:13

回答

4

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

This is a somewhat related question更多详细信息可能适用于您,如果您尝试从隐藏Excel的应用程序运行Excel。

这是给你绝对的有关部分是这样的:

  • 裹使用Excel中的try..catch块捕捉到任何可能的例外每一个功能。
  • 总是通过调用Marshal.ReleaseComObject()明确释放所有Excel对象,然后在您不需要它们时立即将变量设置为null。始终在finally块中释放这些对象以确保失败的Excel方法调用不会导致悬空的COM对象。
  • 发生错误时,请关闭您正在使用的Excel实例。您不可能从与Excel相关的错误中恢复,并且保留该实例的时间越长,使用资源的时间越长。
  • 当您退出Excel时,请确保您防范递归调用的代码 - 如果您的异常处理程序尝试在代码已关闭Excel的过程中关闭Excel,则最终会生成一个死的Excel实例。
  • 调用和GC.WaitForPendingFinalizers()之后立即调用Application.Quit()方法以确保.NET Framework立即释放所有Excel COM对象。

编辑这是你增加了更多的细节,你的问题后。

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

使用COM互操作,您应该创建COM对象尽可能靠近需要它们的位置,并尽快显式释放它们。对于Office应用程序尤其如此。

我让这个类更容易使用COM对象:这个包装是一次性的,所以你可以用你的COM对象使用using(...) - 当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; 
      } 
     } 
    } 
} 
+0

Upvoted,很好的答案。尽管我认为滥用“ComPtr ”这个名字有点多。 (和匈牙利符号) – 2012-03-25 03:02:09

+0

@RitchMelton感谢您的投票。你为什么认为使用'ComPtr '是滥用这个名字? – xxbbcc 2012-03-25 03:58:07