XLDotNet : Quitter Excel sans laisser d'instance en RAM

www.vbfrance.com/code.aspx?id=27541

Par Patrice Dargenton

patrice.dargenton@free.fr

http://patrice.dargenton.free.fr/index.html

www.vbfrance.com/listeauteur2.aspx?ID=1124

 

En suivant scrupuleusement les recommandations de Microsoft (Q317109) pour libérer les ressources liées à l'automation d'Excel en DotNet... Excel persiste quand même en RAM ! (de l'aveu même de Microsoft). Le problème est du au caractère non déterministe du collecteur (ramasse-miette ou garbage collector) de la plateforme .Net (et avec l'interopérabilité COM) : personne ne peut vraiment dire quand une ressource sera libérée ; du coup le serveur COM ne peut pas quitter l'instance d'Excel tant que l'appli .Net qui l'a créée (avec CreateObject ou qui y a accéder avec GetObject), et qui possède une référence dessus, reste invisible en RAM : cela va perturber le fonctionnement d'Excel dès la seconde manipulation (si on utilise GetObject, sinon cela ne perturbe pas Excel mais on laisse une nouvelle instance en RAM à chaque appel). Toutefois, lorsque l'on quitte l'appli .Net, l'instance parasite disparaît automatiquement. Certains affirment qu'en programmant en liaison anticipée (= précoce) via l'interface Office PIA, on peut résoudre ce problème ; cependant cette interface ne fonctionne qu'avec Office XP et 2003, pas avec Office 2000 : la liaison tardive est le seul moyen de cibler toutes les versions d'Excel. Du coup, la seule solution pour éviter de traîner une instance en RAM est de la tuer purement et simplement (quitte à relancer ensuite Excel de façon indépendante via le fichier créé : voir l'exemple). Pour la tuer il suffit de noter son Id de processus. Or, si l'on peut effectivement retrouver cet Id via le handle d'instance, accessible pour Excel XP et 2003, cette technique ne fonctionne pas pour Excel 2000 (le handle d'instance n'est pas accessible dans ce cas, et le handle de fenêtre ne permet pas de retrouver l'Id processus). Et de plus, ce problème concerne tous les logiciels Office. J'ai heureusement trouvé sur le web une astuce qui fonctionne à coup sûr : comparer avant et après la liste des processus pour trouver précisément celui que j'ai créé. En conclusion, ce code est donc incontournable pour tout ceux qui veulent cibler aussi Office 2000 en DotNet.

 

'   ======================================================================================

'   clsExcelHost : Classe pour héberger Excel

'   ============

 

' Title: EXCEL.EXE Process Killer

' Description: After many weeks of trying to figure out why the EXCEL.EXE Process

'  does not want to go away from the Task Manager, I wrote this class that will ensure

'  that the correct EXCEL.EXE Process is closed. This is after using Excel.Application

'  via Automation from a VB.NET/ASP.NET application.

' This file came from Planet-Source-Code.com... the home millions of lines of source code

' You can view comments on this code/and or vote on it at:

' http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=1998&lngWId=10

 

' The author may have retained certain copyrights to this code...

'  please observe their request and the law by reviewing all copyright conditions

'  at the above URL.

 

'   Author: I.W Coetzer 2004/01/22

'   *Thanks Dan for the process idea.

'   Classe commentée et légèrement modifiée par Patrice Dargenton le 05/11/2004

'   *Solution to the EXCEL.EXE Process that does not want to go away from task manager.

'

'   IMPLEMENTATION (EXAMPLE OF THE CLASS IN USE)

'

'Public Sub TestXL()

 

'    Dim oXLH As clsExcelHost

'    Try

'        oXLH = New clsExcelHost

'    Catch

'        MsgBox("Excel n'est pas installé !")

'        Exit Sub

'    End Try

'    oXLH.xlApp.Workbooks.Add()

'    oXLH.xlApp.Range("A1") = "Hello World!"

'    oXLH.xlApp.Workbooks(1).SaveAs("C:\Test.xls")

'    oXLH.xlApp.Workbooks(1).Close()

'    oXLH.xlApp.Quit()

'    oXLH.xlApp = Nothing

'    oXLH.Quitter() ' = Process.GetProcessById(xl.ProcId).Kill()

'    oXLH = Nothing

'    MsgBox("C:\Test.xls a été créé avec succès !")

'    Dim p As New Process

'    p.StartInfo = New ProcessStartInfo("C:\Test.xls")

'    p.Start()

 

'End Sub

'

'   ======================================================================================

 

Public Class clsExcelHost

 

    Public xlApp As Object

    Private ProcId%

 

    Public Sub New()

 

        ProcId = 0

        ' Liste des processus avant le mien

        Dim Process1() As Process = Process.GetProcesses()

        xlApp = CreateObject("Excel.Application")

        ' Liste des processus après le mien : la différence me donnera l'Id du mien

        Dim Process2() As Process = Process.GetProcesses()

 

        Dim i%, j%

        Dim bMonProcessXL As Boolean

        For j = 0 To Process2.GetUpperBound(0)

            If Process2(j).ProcessName = "EXCEL" Then

                bMonProcessXL = True

                ' Parcours des processus avant le mien

                For i = 0 To Process1.GetUpperBound(0)

                    If Process1(i).ProcessName = "EXCEL" Then

                        If Process2(j).Id = Process1(i).Id Then

                            ' S'il existait avant, ce n'était pas le mien

                            bMonProcessXL = False

                            Exit For

                        End If

                    End If

                Next i

                If bMonProcessXL = True Then

                    ' Maintenant que j'ai son Id, je pourrai le tuer

                    '  xlApp.Hinstance ne fonctionne pas avec Excel 2000

                    '  alors que cette méthode marche toujours !

                    ProcId = Process2(j).Id

                    Exit For

                End If

            End If

        Next j

 

    End Sub

 

    Public Sub Quitter()

        If ProcId = 0 Then Exit Sub

        Process.GetProcessById(ProcId).Kill()

    End Sub

 

    'Protected Overrides Sub Finalize()

    '    MyBase.Finalize()

    'End Sub

 

End Class