visual studio office microsoft example c# .net excel com office-interop

c# - studio - ¿Se debe liberar*cada*objeto de interoperabilidad de Excel utilizando Marshal.ReleaseComObject?



microsoft.office.interop.excel visual studio 2017 (2)

Creo que tendrías que llamar a ReleaseComObject en cada objeto COM. Dado que no se recogen basura, la jerarquía de elementos primarios y secundarios realmente no entra en la ecuación: incluso si se libera el objeto principal, no disminuye el recuento de referencias en ningún objeto secundario.

Editar

Por favor, consulte también ¿Cómo puedo limpiar adecuadamente los objetos de interoperabilidad de Excel? . Recientemente me encontré con esta pregunta, y proporcionó una gran cantidad de información sobre el problema de cómo deshacerse de los objetos COM correctamente. Definitivamente marque más allá de la primera respuesta (marcada), porque las otras respuestas van más allá del simple consejo de "no usar dos puntos" y "utilizar ReleaseComObject para cada objeto com".

Volví a visitar esta pregunta, en primer lugar, porque me di cuenta de que, a pesar de ser muy cuidadoso acerca de registrar y eliminar todos mis objetos COM, mis instancias de Excel todavía no se eliminaban correctamente. Resulta que hay formas en las que se pueden crear objetos COM que no son completamente obvios (es decir, se pueden perder objetos COM incluso si nunca se usan dos puntos). Además, incluso si es exhaustivo, si su proyecto crece más allá de un cierto tamaño, la posibilidad de perder un objeto COM se acerca al 100%. Y puede ser muy difícil encontrar el que te perdiste cuando eso sucede. Las respuestas a la pregunta vinculada anteriormente proporcionan algunas otras técnicas para asegurarse de que la instancia de Excel definitivamente se cierra. Mientras tanto, hice una pequeña (pero significativa) actualización de mi ComObjectManager (abajo) para reflejar lo que aprendí de la pregunta vinculada anteriormente.

Pregunta original

He visto varios ejemplos donde Marshal.ReleaseComObject() se usa con objetos de Interoperación de Excel (es decir, objetos del espacio de nombres Microsoft.Office.Interop.Excel), pero lo he visto usado en varios grados.

Me pregunto si puedo salir con algo como esto:

var application = new ApplicationClass(); try { // do work with application, workbooks, worksheets, cells, etc. } finally { Marashal.ReleaseComObject(application) }

O si necesito liberar cada objeto creado, como en este método:

public void CreateExcelWorkbookWithSingleSheet() { var application = new ApplicationClass(); var workbook = application.Workbooks.Add(_missing); var worksheets = workbook.Worksheets; for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++) { var worksheet = (WorksheetClass)worksheets[worksheetIndex]; worksheet.Delete(); Marshal.ReleaseComObject(worksheet); } workbook.SaveAs( WorkbookPath, _missing, _missing, _missing, _missing, _missing, XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing); workbook.Close(true, _missing, _missing); application.Quit(); Marshal.ReleaseComObject(worksheets); Marshal.ReleaseComObject(workbook); Marshal.ReleaseComObject(application); }

Lo que me impulsó a hacer esta pregunta es que, siendo el devoto de LINQ que soy, realmente quiero hacer algo como esto:

var worksheetNames = worksheets.Cast<Worksheet>().Select(ws => ws.Name);

... pero me preocupa que termine con filtraciones de memoria o procesos fantasma si no lanzo cada objeto de hoja de trabajo ( ws ).

Cualquier idea sobre esto sería apreciada.

Actualizar

En base a las respuestas hasta ahora, parece que realmente necesito liberar cada objeto com que creo. Aproveché la oportunidad para crear una clase ComObjectManager para que sea un poco más fácil lidiar con este dolor de cabeza. Debes recordar usar el método Get() cada vez que crees un nuevo objeto com, pero si lo haces, se ocupará de todo lo demás por ti. Avíseme si ve algún problema (o edite y deje un comentario si puede). Aquí está el código:

public class ComObjectManager : IDisposable { private Stack<object> _comObjects = new Stack<object>(); public TComObject Get<TComObject>(Func<TComObject> getter) { var comObject = getter(); _comObjects.Push(comObject); return comObject; } public void Dispose() { // these two lines of code will dispose of any unreferenced COM objects GC.Collect(); GC.WaitForPendingFinalizers(); while (_comObjects.Count > 0) Marshal.ReleaseComObject(_comObjects.Pop()); } }

Aquí hay un ejemplo de uso:

public void CreateExcelWorkbookWithSingleSheet() { using (var com = new ComObjectManager()) { var application = com.Get<ApplicationClass>(() => new ApplicationClass()); var workbook = com.Get<Workbook>(() => application.Workbooks.Add(_missing)); var worksheets = com.Get<Sheets>(() => workbook.Worksheets); for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++) { var worksheet = com.Get<WorksheetClass>(() => (WorksheetClass)worksheets[worksheetIndex]); worksheet.Delete(); } workbook.SaveAs( WorkbookPath, _missing, _missing, _missing, _missing, _missing, XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing); workbook.Close(true, _missing, _missing); application.Quit(); } }


Debería llamar a Marshal.ReleaseComObject en cada objeto COM que use en su código, no solo en el objeto principal de la aplicación.