En realidad, puede liberar su objeto de aplicación de Excel de manera limpia, pero debe tener cuidado.
El consejo de mantener una referencia con nombre para absolutamente cada objeto COM al que acceda y luego liberarlo explícitamente Marshal.FinalReleaseComObject()
es correcto en teoría, pero, desafortunadamente, es muy difícil de administrar en la práctica. Si alguna vez se desliza a algún lado y usa "dos puntos", o itera celdas a través de un for each
bucle, o cualquier otro tipo de comando similar, entonces tendrá objetos COM sin referencia y se arriesgará a bloquearse. En este caso, no habría forma de encontrar la causa en el código; tendría que revisar todo su código a simple vista y, con suerte, encontrar la causa, una tarea que podría ser casi imposible para un gran proyecto.
La buena noticia es que no tiene que mantener una referencia de variable con nombre para cada objeto COM que use. En su lugar, llama GC.Collect()
y luego GC.WaitForPendingFinalizers()
libera todos los objetos (generalmente menores) a los que no tienes una referencia, y luego libera explícitamente los objetos a los que sí tienes una referencia de variable con nombre.
También debe publicar sus referencias nombradas en orden inverso de importancia: primero los objetos de rango, luego las hojas de trabajo, los libros de trabajo y, finalmente, su objeto de aplicación de Excel.
Por ejemplo, suponiendo que tuviera una variable de objeto Range llamada xlRng
, una variable de hoja de trabajo llamada xlSheet
, una variable de xlBook
libro de trabajo llamada y una variable de aplicación de Excel llamada xlApp
, entonces su código de limpieza podría tener el siguiente aspecto:
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(xlRng);
Marshal.FinalReleaseComObject(xlSheet);
xlBook.Close(Type.Missing, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(xlBook);
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
En la mayoría de los ejemplos de código que verá para limpiar objetos COM de .NET, las llamadas GC.Collect()
y GC.WaitForPendingFinalizers()
se realizan DOS VECES como en:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Sin embargo, esto no debería ser necesario, a menos que esté utilizando Visual Studio Tools para Office (VSTO), que utiliza finalizadores que hacen que se promueva un gráfico completo de objetos en la cola de finalización. Dichos objetos no se liberarán hasta la próxima recolección de basura. Sin embargo, si no está usando VSTO, debería poder llamar GC.Collect()
y GC.WaitForPendingFinalizers()
solo una vez.
Sé que llamar explícitamente GC.Collect()
es un no-no (y ciertamente hacerlo dos veces suena muy doloroso), pero para ser sincero, no hay forma de evitarlo. A través de las operaciones normales, generará objetos ocultos a los que no tiene referencias que, por lo tanto, no puede liberar por ningún otro medio que no sea llamar GC.Collect()
.
Este es un tema complejo, pero esto es todo lo que hay que hacer. Una vez que establezca esta plantilla para su procedimiento de limpieza, puede codificar normalmente, sin necesidad de envoltorios, etc.
Tengo un tutorial sobre esto aquí:
Automatización de programas de Office con VB.Net / COM Interop
Está escrito para VB.NET, pero no se desanime por eso, los principios son exactamente los mismos que cuando se usa C #.