Você pode realmente liberar seu objeto Aplicativo do Excel de maneira limpa, mas é necessário ter cuidado.
O conselho para manter uma referência nomeada para absolutamente todos os objetos COM que você acessa e depois liberá-lo explicitamente Marshal.FinalReleaseComObject()
é correto em teoria, mas, infelizmente, é muito difícil de gerenciar na prática. Se alguém deslizar para algum lugar e usar "dois pontos" ou iterar células por meio de umfor each
iterar loop ou qualquer outro tipo de comando semelhante, você terá objetos COM não referenciados e corre o risco de travar. Nesse caso, não haveria maneira de encontrar a causa no código; você precisaria revisar todo o seu código a olho nu e, esperançosamente, encontrar a causa, uma tarefa que poderia ser quase impossível para um projeto grande.
A boa notícia é que você realmente não precisa manter uma referência de variável nomeada para todos os objetos COM que você usa. Em vez disso, chame GC.Collect()
e, em seguida, GC.WaitForPendingFinalizers()
libere todos os objetos (geralmente menores) aos quais você não mantém uma referência e libere explicitamente os objetos aos quais você mantém uma referência de variável nomeada.
Você também deve liberar suas referências nomeadas em ordem inversa de importância: alcance primeiro os objetos, depois as planilhas, as pastas de trabalho e, finalmente, o objeto Aplicativo do Excel.
Por exemplo, supondo que você tenha uma variável de objeto Range denominada xlRng
, uma variável de planilha denominada xlSheet
, uma variável de pasta de trabalho denominada xlBook
e uma variável de aplicativo do Excel denominada xlApp
, seu código de limpeza poderá ter a seguinte aparência:
// 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);
Na maioria dos exemplos de código que você verá para limpar objetos COM do .NET, as chamadas GC.Collect()
e GC.WaitForPendingFinalizers()
são feitas DUAS VEZES como em:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Entretanto, isso não deve ser necessário, a menos que você esteja usando o VSTO (Visual Studio Tools for Office), que usa finalizadores que fazem com que um gráfico inteiro de objetos seja promovido na fila de finalização. Esses objetos não seriam liberados até a próxima coleta de lixo. No entanto, se você não estiver usando o VSTO, poderá ligar GC.Collect()
e GC.WaitForPendingFinalizers()
apenas uma vez.
Eu sei que ligar explicitamente GC.Collect()
é um não-não (e certamente fazê-lo duas vezes parece muito doloroso), mas não há como contornar isso, para ser honesto. Por meio de operações normais, você gerará objetos ocultos aos quais não mantém nenhuma referência que, portanto, não pode liberar por qualquer outro meio que não seja a chamada GC.Collect()
.
Este é um tópico complexo, mas isso realmente é tudo o que existe. Depois de estabelecer esse modelo para o procedimento de limpeza, você pode codificar normalmente, sem a necessidade de invólucros, etc. :-)
Eu tenho um tutorial sobre isso aqui:
Automatizando programas do Office com interoperabilidade VB.Net / COM
Ele foi escrito para o VB.NET, mas não se deixe levar por isso, os princípios são exatamente os mesmos que quando se usa C #.