Puoi effettivamente rilasciare l'oggetto Applicazione Excel in modo pulito, ma devi prenderti cura.
Il consiglio di mantenere un riferimento denominato per qualsiasi oggetto COM a cui si accede e di rilasciarlo esplicitamente tramite Marshal.FinalReleaseComObject()
è in teoria corretto, ma, sfortunatamente, molto difficile da gestire in pratica. Se uno scivola da nessuna parte e usa "due punti", o scorre le celle tramite un for each
ciclo o qualsiasi altro tipo di comando simile, allora avrai oggetti COM senza riferimento e rischierai un blocco. In questo caso, non ci sarebbe modo di trovare la causa nel codice; dovresti rivedere tutto il tuo codice a occhio e sperare di trovare la causa, un compito che potrebbe essere quasi impossibile per un grande progetto.
La buona notizia è che in realtà non è necessario mantenere un riferimento alla variabile denominata per ogni oggetto COM che si utilizza. Invece, chiama GC.Collect()
e quindi GC.WaitForPendingFinalizers()
rilasciare tutti gli oggetti (di solito minori) su cui non hai un riferimento, quindi rilascia esplicitamente gli oggetti su cui hai un riferimento a una variabile denominata.
Dovresti anche rilasciare i riferimenti nominati in ordine inverso di importanza: prima intervallo oggetti, quindi fogli di lavoro, cartelle di lavoro e infine l'oggetto Applicazione Excel.
Ad esempio, supponendo che tu avessi una variabile oggetto Range denominata xlRng
, una variabile Foglio di lavoro denominata xlSheet
, una variabile Workbook denominata xlBook
e una variabile Applicazione Excel denominata xlApp
, il tuo codice di pulizia potrebbe essere simile al seguente:
// 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);
Nella maggior parte degli esempi di codice che vedrai per ripulire gli oggetti COM da .NET, le chiamate GC.Collect()
e GC.WaitForPendingFinalizers()
vengono effettuate DUE VOLTE come in:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Ciò non dovrebbe essere richiesto, tuttavia, a meno che non si utilizzi Visual Studio Tools for Office (VSTO), che utilizza finalizzatori che promuovono un intero grafico di oggetti nella coda di finalizzazione. Tali oggetti non sarebbero stati rilasciati fino alla prossima raccolta dei rifiuti. Tuttavia, se non si utilizza VSTO, si dovrebbe essere in grado di chiamare GC.Collect()
e GC.WaitForPendingFinalizers()
solo una volta.
So che chiamare esplicitamente GC.Collect()
è un no-no (e certamente farlo due volte suona molto doloroso), ma non c'è modo di aggirarlo, a dire il vero. Attraverso le normali operazioni genererai oggetti nascosti ai quali non detieni alcun riferimento che, pertanto, non puoi rilasciare con nessun altro mezzo diverso dalla chiamata GC.Collect()
.
Questo è un argomento complesso, ma questo è davvero tutto quello che c'è da fare. Una volta stabilito questo modello per la procedura di pulizia, è possibile codificare normalmente, senza la necessità di wrapper, ecc. :-)
Ho un tutorial su questo qui:
Automatizzare i programmi di Office con VB.Net / COM Interop
È scritto per VB.NET, ma non scoraggiarlo, i principi sono esattamente gli stessi di quando si usa C #.