Xldotnet : quitter excel sans laisser d'instance en ram

Description

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.

Par Patrice Dargenton

Source / Exemple :


'   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

Conclusion :


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

Codes Sources

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.