Tuer processus cachés

Bonjour à tous, bonjour le fofo,

J'ai développé un outil à la base sur XP et Excel 2003 qui fonctionne très bien. Dans cet outil j'ouvre certains classeurs en caché (bases de donnée) pour importer certaines informations dans ma feuille principale. Les classeurs cachés sont ensuite fermés par un code que j'ai récupéré sur le net et qui ventile tous les processus Excel et si la fenêtre de ce dernier est cachée, le processus est fermé automatiquement.

Le problème c'est que ce code en question (qui fonctionne bien sur XP) ne fonctionne plus sur Windows 7 (aussi bien 32bits que 64bits d'ailleurs). Du coup, en cherchant bien, j'en ai trouvé un qui fonctionne et qui tue les processus sous Windows 7, sauf qu'il ne fait pas la différence entre fenêtre visible et fenêtre invisible. Évidemment j'ai besoin qu'il ne ferme que les sessions d'excel ou word invisibles à l'utilisateur.

Ci-dessous le code qui fonctionne aussi bien sur xp et win7 :

Option Explicit

Type strucPROCESSENTRY32
    dwSize As Long              ' DWORD : Size of the structure, in bytes
    cntUsage As Long            ' DWORD : not used (0)
    th32ProcessID As Long       ' DWORD : PID
    th32DefaultHeapID As Long   ' ULONG_PTR : not used (0)
    th32ModuleID As Long        ' DWORD : not used (0)
    cntThreads As Long          ' DWORD : Threads
    th32ParentProcessID As Long ' DWORD : parent process ID
    pcPriClassBase As Long      ' LONG
    dwFlags As Long             ' LONG : not longer used (0)
    szExeFile As String * 512   ' TCHAR szExeFile[MAX_PATH] name of the executable file for the process
End Type

Const TH32CS_SNAPPROCESS As Long = &H2
Const PROCESS_TERMINATE As Long = &H1
Const PROCESS_QUERY_INFORMATION As Long = &H400

Private Declare Function CreateToolhelp32Snapshot Lib "Kernel32.dll" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
Private Declare Function Process32First Lib "Kernel32.dll" (ByVal hSnapshot As Long, ByRef lppe As strucPROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "Kernel32.dll" (ByVal hSnapshot As Long, ByRef lppe As strucPROCESSENTRY32) As Long
Private Declare Function OpenProcess Lib "Kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessID As Long) As Long
Private Declare Function CloseHandle Lib "Kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function TerminateProcess Lib "Kernel32.dll" (ByVal hProcess As Long, ByVal dwExitCode As Long) As Long

Sub TermProcess(strProcessName As String)
Dim hSnapshot As Long
Dim lppe As strucPROCESSENTRY32
Dim hProc As Long
Dim Retval As Long
Dim strPrcsName As String

    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
    lppe.dwSize = Len(lppe)
    Retval = Process32First(hSnapshot, lppe)

    Do While Retval
        strPrcsName = Left(lppe.szExeFile, InStr(1, lppe.szExeFile, vbNullChar) - 1)
        Debug.Print strPrcsName, lppe.th32ProcessID

        If strPrcsName Like strProcessName Then
            hProc = OpenProcess(PROCESS_TERMINATE, 0, lppe.th32ProcessID)
            If hProc <> 0 Then
                Retval = TerminateProcess(hProc, 0)
                ' Si Retval=0 échec de la fonction TerminateProcess(..)
                Call CloseHandle(hProc)
            End If
        End If
        ' Process Suivant
        Retval = Process32Next(hSnapshot, lppe)
    Loop

    Call CloseHandle(hSnapshot)

End Sub

Function KillProcess(pLng_ProcessId As Long)
Dim hProc As Long
Dim Retval As Long

    hProc = OpenProcess(PROCESS_TERMINATE, 0, pLng_ProcessId)
    If hProc <> 0 Then
        Retval = TerminateProcess(hProc, 0)
        ' Si Retval=0 échec de la fonction TerminateProcess(..)
        Call CloseHandle(hProc)
    End If
End Function

Private Sub test()
    'Nécessite d'activer la référence Microsoft Office Word
    Dim oAppWord As Word.application
    Dim oDocWord As Word.Document

    Set oAppWord = New Word.application
    Set oDocWord = oAppWord.Documents.Add
    With oAppWord
       .Visible = False
       .WindowState = wdWindowStateMaximize
    End With

    TermProcess "WINWORD.EXE"
End Sub

Si quelqu'un a une idée sur "comment détecter la visibilité de la fenêtre d'un processus particulier", je suis preneur. Merci beaucoup

Bonjour,

J'ai bien ma petite idée sur ton problème mais il faudrait confirmation.

Tu peu montrer le code qui ouvre et ferme tes classeurs ?

Tu dis.

Ci-dessous l'ancien code qui faisait cette tâche (fonctionne bien sur XP mais par sur Win7)

Je lançais la procédure sub_fermerExcelInvisible() pour supprimer tous les processus cachés d'Excel

Option Explicit

'API ouverture processus et ses constantes
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessID As Long) As Long
Public Const PROCESS_VM_READ As Long = (&H10)
Public Const PROCESS_QUERY_INFORMATION As Long = (&H400)
Public Declare Function GetCurrentProcessId Lib "Kernel32.dll" () As Long
Declare Function IsWindowVisible Lib "user32" (ByVal Hwnd As Long) As Long
Declare Function GetModuleFileNameEx Lib "psapi.dll" Alias "GetModuleFileNameExA" (ByVal hProcess As Long, ByVal hModule As Long, ByVal lpFilename As String, ByVal nSize As Long) As Long
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowThreadProcessId Lib "user32" (ByVal Hwnd As Long, lpdwProcessId As Long) As Long
Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal Hwnd As Long, ByVal lpClassName As String, ByVal nmaxCount As Long) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal Hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long

Const PROCESS_TERMINATE As Long = &H1
Public Const MAX_PATH As Long = 260
Public hProcess As Long    'handle du processus
Public CurrentProcessId As Long, ViewOnly As Boolean
Public ExcelAppli As Object, OpFichier As Object, bob As Long

Sub sub_fermerExcelInvisible()
    '-------------------------------------------------------------------------
    ' Ferme les fichiers excel cachés et ouverts en lecture seule (ex: la bdd)
    '-------------------------------------------------------------------------
    Dim lRet As Long
    Dim lParam As Long
    CurrentProcessId = GetCurrentProcessId()
    lRet = EnumWindows(AddressOf EnumWinProc, lParam)
End Sub

Function EnumWinProc(ByVal lhWnd As Long, ByVal lParam As Long) As Long
    '-----------------------------------
    'enumeration des processus
    'renvoie du/des session(s) excel
    'Arret du/des Processus excel caché
    '-----------------------------------
    Dim Retval As Long, ProcessID As Long, ThreadID As Long
    Dim WinClassBuf As String * 255, WinTitleBuf As String * 255
    Dim WinClass As String, WinTitle As String, NomExe As String

    ' see the Windows Class and Title for each top level Window
    Retval = GetClassName(lhWnd, WinClassBuf, 255)
    WinClass = StripNulls$(WinClassBuf)  ' remove extra Nulls & spaces
    Retval = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = StripNulls$(WinTitleBuf)
    ' la fenetre(thread principale)est elle visible ?
    Retval = IsWindowVisible(lhWnd)
    ' on recupere l'PID de la fenetre(thread principale)
    ThreadID = GetWindowThreadProcessId(lhWnd, ProcessID)
    ' on recupere le nom du thread principale
    NomExe = GetProcessFileName(ProcessID)
        If NomExe = "excel.exe" And WinTitle Like "Microsoft Excel*" = True Then
            ' si le thread principale d'excel est invisible
            If Retval = 0 Then
                If ViewOnly = False Then
                    ' on arrete le processus
                    ' La fonction renvoie 1 si le processus c'est arrete
                    If CloseProcess(ProcessID) = 1 Then
                    Else
                        MsgBox "le processus excel.exe invisible n'a pas pu etre arreté ! ", vbExclamation
                    End If
                End If
            End If
        End If
    CloseHandle hProcess
    EnumWinProc = True
End Function

Public Function StripNulls(OriginalStr As String) As String
   ' This removes the extra Nulls so String comparisons will work
   If (InStr(OriginalStr, Chr(0)) > 0) Then
      OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
   End If
   StripNulls = OriginalStr
End Function

Public Function GetProcessFileName(ByVal ProcessID As Long) As String
    ' Processus 0
    If ProcessID = 0 Then
        GetProcessFileName = "[System Process]"
    ' Processus 4
    ElseIf ProcessID = 4 Then
        GetProcessFileName = "System"
    Else
        ' On cherche son chemin d'accès complet
        'Dim hProcess As Long    'handle du processus
        Dim hModule As Long    'handle du module de l'exe
        Dim ret As Long        'résultat
        ' On demande un handle pour le processus
        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ Or PROCESS_TERMINATE, 0&, ProcessID)
        ' Si erreur (accès refusé)
        If hProcess Then
            ' On préformate la chaine
            GetProcessFileName = Space(MAX_PATH)
            ' On récupère son nom complet
            GetModuleFileNameEx hProcess, 0, GetProcessFileName, MAX_PATH
            ' On ferme le handle ouvert
            'CloseHandle hProcess
            ' On retire le vbNUllChar de fin de chaine
            '-----------------------------------
            'LES LIGNES CI-DESSOUS BUGGUENT SUR WINDOWS 7
           '-----------------------------------
            GetProcessFileName = Left(GetProcessFileName, InStr(GetProcessFileName, vbNullChar) - 1)
            'on extrait le nom de l'Image du processus

            GetProcessFileName = LCase(Right(GetProcessFileName, InStr(1, StrReverse(GetProcessFileName), "\") - 1))
            Exit Function
        ElseIf hProcess = 0 Then
            GetProcessFileName = vbNullString
        End If
    End If
End Function

Public Function CloseProcess(ProcessID As Long) As Long
    'fermeture du thread principal d'excel
    CloseProcess = TerminateProcess(hProcess, 0)
End Function

Tu ne donne que les procédures pour tuer les processus mais ce n'est pas cela que je demandais.

Chrix a écrit :

j'ouvre certains classeurs en caché (bases de donnée) pour importer certaines informations dans ma feuille principale

C'est ce code que je voudrais voire.

Pourquoi employer un procédé aussi complexe pour extraire des données d'autre classeurs ?

Tu dis.

Si ca peux te rassurer, j'ouvre bien et je ferme bien tous mes objets et autres classeurs externes par le code normal. Le problème c'est que si je rencontre un bug quelconque avant d'agir sur la destruction de mon objet, mon objet se détruit ou n'existe plus. Du coup je ne peux plus le fermer correctement et le processus reste actif. C'est pour cela que j'utilisais cette fonction afin de bien tout nettoyer et être sûr qu'il n'existe plus aucun processus caché. regarde la procédure sub_deconnexionBdd() pour mieux comprendre.

Exemple :

Option Explicit
Sub sub_connexionBdd()
    '-------------------------------------------------------------------------------------------
    ' Procédure de connexion à la base de donnée
    '-------------------------------------------------------------------------------------------
    On Error Resume Next

    'Déclarations
    Dim sCheminBdd As String, sRepBdd As String
    Dim bConnexion As Boolean
    Dim rRepertoireBdd As Range, rCell As Range

    'Chargement des variables globales
    sub_chargerVariable

    'Affectation et liste des répertoires possibles
    Set rRepertoireBdd = ThisWorkbook.Worksheets("param_program").[paramListeRepertoireBdd2]

    'Création de l'objet BDD
    Set gv_oBDDapp = CreateObject("Excel.Application")

    'Tentative de connexion dans tous les répertoires possibles
    'Si un fichier est trouvé => sortie de la boucle
    bConnexion = False
    For Each rCell In rRepertoireBdd
        sRepBdd = rCell.Value
        sCheminBdd = rCell.Value & [paramFichierBdd].Value
        Set gv_oBDD = gv_oBDDapp.Workbooks.Open(sCheminBdd, ReadOnly:=True)
        If err = 0 Then
            bConnexion = True
            Exit For
        Else
            err.Clear
        End If
    Next rCell

    On Error GoTo 0

    'Resultat
    If Not bConnexion Then
        [paramFichierActuelBdd].Value = vbNullString
        [paramRepertoireActuelBdd].Value = vbNullString
        [paramVerifFichierBdd].Value = vbNullString
        MsgBox "Impossible d'ouvrir la base de donnée." & vbNewLine & "Le fichier est ouvert mais sera totalement inutilisable", vbCritical
    Else
        'stockage du chemin accessible dans les paramètres de la feuille
        [paramFichierActuelBdd].Value = sCheminBdd
        [paramRepertoireActuelBdd].Value = sRepBdd
        [paramVerifFichierBdd].Value = [paramFichierBdd].Value
    End If

End Sub

Sub sub_deconnexionBdd()
    '-------------------------------------------------------------------------------------------
    ' Procédure de déconnexion à la base de donnée, (fermer le fichier invisible)
    '-------------------------------------------------------------------------------------------
    If gv_bErreurPerso Then On Error GoTo erreur

    On Error Resume Next
    If Not gv_oBDD Is Nothing Then
        gv_oBDD.Close False
        gv_oBDDapp.Quit

        Set gv_oBDD = Nothing
        Set gv_oBDDapp = Nothing
        On Error GoTo erreur
    Else
        sub_fermerExcelInvisible
    End If

    Exit Sub
erreur:
    sub_gestionnaireErreur err, "mod_bdd", "sub_deconnexionBdd"
End Sub

Voilà pourquoi je voudrais pouvoir fermer les processus cachés coute que coute.

Chrix a écrit :

Si ca peux te rassurer, j'ouvre bien et je ferme bien tous mes objets et autres classeurs externes par le code normal

Alors là.. si tu trouve ça un code normal pour ouvrir et fermer des classeurs je pense que tu est non seulement à côté de la plaque mais à côté de la planète.

Pour un "code normal", toutes tes procédure peuvent être réduites à une ou deux dizaine de lignes.

Si tu est intéressé je peu voir pour optimiser une procédure. Sinon beh... je ne peu t'aider plus.

Tu dis.

mdrr !!

Bon lermite, c'est gentil mais je crois que tu m'as apporté le maximum que tu pouvais faire, hein on va pas insister.

Mais attention quand même à être respectueux des autres.

Si je peux te donner un conseil, essaie de bien comprendre/lire les problèmes des autres (c'est important pour bien répondre en fait) avant de porter des jugements ou d'affirmer "arbitrairement" (pour ne pas dire autre chose hein, je suis poli moi) qu'une bonne procédure est une procédure avec peu de lignes...

D'autant que je ne cherche pas à optimiser une procédure mais surtout en avoir une autre qui d'abord fonctionne aux exigences de Windows 7.

Mais merci quand même.

Bref, ya t'il un spécialiste des API x64 qui pourrait m'aider ?

Je ne vois pas où j'ai été impoli, j'ai juste donné mon avis sur ta façon de faire, mais si cela t'a froisser tu voudras bien m'excuser ?

Et tu peu être mort de rire jusqu'a la saint glinglin c'est bien la toute première fois que je vois employer des api pour ouvrir des classeurs Excel., pourtant crois moi... les API.. ça me connais, j'ai bien décodé toutes tes magnifiques procédures.

Mais il faut reconnaître que tout le monde ne peu pas être aussi calé que toi.

Cordialement.

Bon, déjà j'utilise pas cet API pour ouvrir des classeurs mais simplement pour fermer les sessions d'Excel qui resteraient ouvertes suite à un bug.

Si tu parles d'un workbooks.open ou d'un workbook.close ou app.quit, je le fais déjà pour créer/détruire mon objet (c'est pour ca que je te demandais de regarder ma fonction sub_deconnexionBdd()... )

Bref, je n'utilise cet API si et seulement si mon objet est détruit/perdu suite à un bug. La connexion avec la base de donnée est persistante (toujours en fond de tache) et c'est pour ça que je veux qu'elle soit cachée (et c'est d'ailleurs pour ça que j'utilise CreateObject("Excel.Application") pour créer mon objet)

Donc si tu veux mon problème n'est pas un problème de conception, mais un problème d'incompatibilité avec Win 7 sur une vieille procédure que je veux remplacer par une nouvelle (que j'ai déjà trouvé, voir plus haut) mais qui ne gère pas la fermeture des processus cachés.

Voilà, telle était ma question de départ que j'aimerais bien élucider.

Bon, puisque tu répond, restons constructif et oublions les deux postes précédant.

Chrix a écrit :

Voilà, telle était ma question de départ.

J'avais très bien compris ta question de départ (sinon je ne serais pas intervenu) et le code qui va avec, je n'ai pas répondu dans ce sens parce que je n'ai pas encore eu l'occasion de faire des recherches sur ce point bien précis et que je trouvais ( et trouve toujours ) que ton approche n'est pas la bonne.

Tu crée une nouvelle instance d'Excel pour chaque classeur qui fait partie de ta BD.. Pourquoi ? alors qu'il serait si facile de l'ouvrir dans la même instance que le classeur principal ? . Si une erreur devait se produire... disons ouvrir un classeur qui n'existe pas/plus le traitement d'erreur local aurais vite fait d'y remédier et il n'y aurait pas d'instance d'Excel qui... traînerait.

Qu'en pense-tu ?

Oui, restons constructif.

Pour résumer et situer rapidement ce que fais mon application. C'est donc une application de chiffrage, il y a plusieurs fichiers excel qui ont été créées par des commerciaux mais dans un format toujours uniforme. Ce sont des fichiers d'articles (avec référence, prix de revient, quantité etc) et il y en a autant de fichiers qu'il y a de catégorie d'article. Alors en soi, ce ne sont pas de vraies bases de donnée mais elles ont le mérite d'être standardisées.

Avant ils allaient chercher les articles en ouvrant les BDD une par une et copiaient collaient leur bloc d'article dans leur grille de chiffrage finale.

Donc ça c'était avant. Désormais, j'ai automatisé tout ça en leur créant, dans un fichier excel principal, un module de recherche (style google) qui leur permet facilement de trouver/d'importer des articles (et leur éviter ainsi l'ouverture manuelle du fichier et le copier-coller d'article)

C'est à ce moment que mon code intervient. Lorsque le commercial fait une recherche depuis son onglet de chiffrage, le code ouvre une nouvelle instance d'excel en invisible avec le fichier en lecture seule, scanne les articles qui correspond à sa recherche et lui propose les résultats avant de se fermer. C'est très important (et j'insiste ^^) que le fichier d'article reste caché afin que le commercial reste sur son onglet et ne soit pas perturbé par un fichier qui s'ouvre et qui se ferme à chaque fois qu'il fait une recherche. Tout doit être le plus transparent possible pour l'utilisateur.

Seulement voilà, autant ca marche impeccable sous Win XP autant j'ai constaté que sous windows 7 certains bugs mineurs font parfois planter la recherche de l'utilisateur et même avec une gestion d'erreur personnalisée, certaines sessions excel cachées ne se ferment pas correctement. Le temps de corriger tous ces petits bugs, j'ai trouvé cette parade par l'API qui en cas de problème inattendu (si l'objet n'existe plus) permet de supprimer les sessions d'excel qui trainent.

Alors évidemment, je fais peut être fausse route, peut être qu'il y a moyen d'ouvrir un classeur excel masqué dans la session active de l'utilisateur mais tout mon code est basé sur ce système et si je pouvais ne pas tout réécrire, ça serait un gros gain de temps.

Cela dit et maintenant que le problème est posé plus clairement, comment procèderais tu pour arriver aux mêmes fins avec une seule instance active ?

Je ferais comme ça..

Copie les deux classeurs dans le même répertoire et ouvre le classeur Principale.xls

J'ai mis en xls vu que je ne sais pas si ta version d'Excel est encore 2003 ou +

Et clic sur le bouton.

Attention, quelque restriction:

Si les noms de feuilles des BD change il faudra la mettre en plus, tu dis.

Les plages sont passées en string, les plages réel étant difficile à adapter sur des classeurs invisible mais en principe ça ne pose aucun problème.

Tu pourrais éventuellement passer la sub en fonction avec les données en paramètre, pour que ce soit clair j'ai laissé en sub.

Bien entendu ce n'est qu'une démo, il faudra l'adapter à tes besoins.

Tu dis.

36principale.zip (11.56 Ko)
35donnee.xls (16.00 Ko)

Ok j'ai bien compris le principe.

Toutefois, j'ai très peur concernant ce ActiveWindow.Visible = False qui à l'instant viens de cacher mon classeur principal (que j'étais en train de triturer). Pour peu qu'elle soit combinée avec un screenupdating = false, on ne peut plus agir sur rien. Si cela arrive à un des mes collaborateurs, je vais passer à la moulinette

Je ne vais pas prendre le risque de l'utiliser. Cela dit, si tout est bien maitrisé, c'est une solution qui marche.

Merci de m'avoir présenté cette solution que je ne connaissais pas.

Evidemment, si tu est en train de "triturer" les instances sont perdues. Tes utilisateurs vont-ils aller Triturer ton code ?

MAIS OK, je comprend, pourquoi faire simple quand il y a moyen de faire compliquer.

A++

lol

Dans le monde professionnel, j'ai appris une chose importante " les utilisateurs sont capables de tout ", et si ce genre de problème m'arrive à moi, il peux arriver à tout le monde. Et puis je ne suis qu'un humain, le code parfait n'existe pas, on fait toujours des erreurs et je ne suis pas à l'abri d'un activewindow mal placé, mal maitrisé. En tout cas, même en allant au devant de tout ce qu'un utilisateur peut faire, il y a toujours une situation que l'on a pas prévu.

Je ne critique pas le code que tu as fourni, il marche très bien dans ta démo (et je t'ai d'ailleurs remercié pour cela...) mais je n'ai pas la volonté de modifier une centaine de procédure sur une fonction que, pour l'instant, je ne maitrise pas.

C'est simplement un choix par rapport à mon projet déjà existant et à mon habitude de ne jamais faire confiance aux objets de type 'active' dans ma programmation (qui m'ont souvent causé des problèmes).

J'ai trouvé la solution, inclure le code ci-dessous dans un module :

Option Explicit
Declare Function FindWindow Lib "USER32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Declare Function GetWindowText Lib "USER32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function GetWindow Lib "USER32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Declare Function IsWindowVisible Lib "USER32" (ByVal hWnd As Long) As Long
Declare Function PostMessage Lib "USER32" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const WM_QUIT = &H12

Sub FermerFenetreInvisible(sNomFenetre As String)
'---------------------------------------------------------------------------------------
' Procedure : FermerFenetreInvisible
' Date      : 16/04/2012
' Purpose   : Ferme les fenêtres cachées avec une partie du nom de fenêtre passé en argument (sans le "voulez vous enregistrer..." éventuel)
'---------------------------------------------------------------------------------------
    Dim titre As String
    Dim hWnd As Long
    hWnd = FindWindow(0, 0)

    Do While hWnd <> 0
        If IsWindowVisible(hWnd) = 0 Then
            'Si n'est pas visible => récupération du nom de la fenêtre
            titre = String(100, Chr$(0))
            GetWindowText hWnd, titre, 100
            titre = Left$(titre, InStr(titre, Chr$(0)) - 1)
            If LCase(titre) Like "*" & LCase(sNomFenetre) & "*" Then
                'Fenêtre correspondante trouvée
                'Fermeture sans le "voulez vous enregistrer..." grace à la constante WM_QUIT
                PostMessage hWnd, WM_QUIT, 0&, 0&
            End If
        End If
        hWnd = GetWindow(hWnd, 2)
    Loop
End Sub

Sub testFermetureWord()
    'Nécessite la référence Microsoft Word x.0 object library
    Dim oAppWord As Word.application
    Dim oAppWord2 As Word.application
    Set oAppWord = CreateObject("Word.Application")
    Set oAppWord2 = CreateObject("Word.Application")
    'on met une application visible et une autre invisible
    oAppWord2.Visible = True
    oAppWord.Visible = False

    MsgBox "ouvrez le gestionnaire des tâches"
    FermerFenetreInvisible "microsoft word"
End Sub
Rechercher des sujets similaires à "tuer processus caches"