2012-03-24 4 views
0

Допустим, что у меня есть один компонент, который что-то делает с объектом Workbook и где-то посередине этого тела метода. Я вызываю какой-то метод другого класса. Например:COM-объект excel interop clean

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 (документ); Я использую документ, и я повторяю его рабочие листы.

EDIT Более конкретный вопрос. Должен ли я вызвать ReleaseCOMObject в документе и на листах в otherComponent.MethodCall (документ) или нет?

У меня никогда не было хорошего объяснения, как мне управлять этим неуправляемым кодом. Я был бы очень признателен, если бы кто-нибудь мог мне это объяснить.

+0

В общем, метод, который создал объект, должен отвечать за очистку. Это несколько расплывчато, что состоит из «очистки» в этом сценарии. Вы должны опубликовать код инициализации и очистки, а также некоторое объяснение того, что вы делаете, что «может вызвать проблемы позже». –

ответ

4

Вам придется выпустить все локальные объекты вручную в области, где вы их создаете. При использовании приложений 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; 
      } 
     } 
    } 
} 
+0

Упрощенный, хороший ответ. Хотя я думаю, что злоупотреблять именем 'ComPtr ' немного. (И венгерская нотация) –

+0

@RitchMelton Спасибо. Почему вы думаете, что использование 'ComPtr ' злоупотребляет именем? – xxbbcc

 Смежные вопросы

  • Нет связанных вопросов^_^