COM-объект Excel Interop очистить - PullRequest
0 голосов
/ 24 марта 2012

Допустим, у меня есть один компонент, который делает что-то с объектом 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);
  }
}

И в этом другом компоненте. Метод вызова (документ); Я использую документ и перебираю его листы.

РЕДАКТИРОВАТЬ Чтобы быть более конкретным в вопросе. Должен ли я вызвать ReleaseCOMObject для документа и для рабочих таблиц в otherComponent.MethodCall (документ) или нет?

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

1 Ответ

4 голосов
/ 24 марта 2012

Вы должны будете освободить все локальные объекты вручную в области, в которой вы их создали.При использовании приложений Office через автоматизацию не полагайтесь на сборщик мусора для очистки этих объектов - даже если он правильно его выполняет, может потребоваться время, чтобы его сработало, и вы можете получить временные объекты, содержащие ссылки на другие объекты.что, по вашему мнению, уже ушло.

Это несколько связанный вопрос с более подробной информацией, которая может быть применима к вам, если вы попытаетесь запустить Excel из своего приложения со скрытым Excel.

Часть, которая определенно относится к вам, такова:

  • Оберните каждую отдельную функцию, использующую Excel, в блок try..catch, чтобы захватить любое возможное исключение.
  • Всегда явно освобождатьвсе объекты Excel, вызвав Marshal.ReleaseComObject(), а затем установив переменные на null, как только они вам не понадобятся.Всегда выпускайте эти объекты в блоке finally, чтобы убедиться, что неудачный вызов метода Excel не приведет к зависанию COM-объекта.
  • При возникновении ошибки закройте используемый экземпляр Excel.Маловероятно, что вы сможете восстановить ошибки, связанные с Excel, и чем дольше вы сохраняете экземпляр, тем дольше он использует ресурсы.
  • При выходе из Excel убедитесь, что вы защищаете этот код от рекурсивных вызовов - если вашобработчики исключений пытаются завершить работу Excel, когда ваш код уже находится в процессе завершения работы Excel, в результате вы получите мертвый экземпляр Excel.
  • Вызов GC.Collect() и GC.WaitForPendingFinalizers() сразу после вызова *Метод 1024 *, чтобы убедиться, что .NET Framework немедленно выпускает все COM-объекты Excel.

Редактировать : это после того, как вы добавили больше деталей к своему вопросу.

В 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;
            }
        }
    }
}
...