VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

corion

XLDnaute Nouveau
bonjour à tous,
j'ai une macro VBA qui envoie un fichier via thunderbird dans le genre :
Shell("C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe -compose attachment= .....", 1)
je dois supprimer ledit fichier après envoi ;
pour détecter la fin du shell, j'ai trouvé le truc suivant :

------------------------------------------------------
Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long

Sub AttendreShell(IdApp As Long)
Const PROCESS_QUERY_INFORMATION As Long = &H400
Const STILL_ACTIVE As Long = &H103
Dim lPtrProcess As Long
Dim lExitCode As Long
Dim lResult As Long

lPtrProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0&, IdApp)
Do
lResult = GetExitCodeProcess(lPtrProcess, lExitCode)
DoEvents
Loop While lExitCode = STILL_ACTIVE
End Sub


Sub test()
Dim IdApp As Long
'IdApp = Shell("c:\windows\system32\notepad.exe", 1)
IdApp = Shell("C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe -compose ", 1)
AttendreShell IdApp
Debug.Print "terminé"
End Sub

----------------------------------------------------------------------------
ça marche nickel avec notepad, qui supporte plusieurs instances du programme

mais avec thunderbird, deux cas possibles :
- thunderbird n'était pas déjà ouvert : il s'ouvre en mode édition , et la fermeture de la fenêtre est bien détectée : ça fonctionne bien !
- thunderbird était déjà ouvert : le lancement du shell ouvre une seconde instance, qui est automatiquement tuée (1 seule instance autorisée) : la fin du programme est détectée avant même l'affichage de la fenêtre édition ; je supprime mon fichier avant l'envoi : envoi impossible.

je ne veux pas fermer préalablement et systématiquement thunderbird, qui est peut être déjà en cours d'édition, ou de lecture de message ou d'agenda ;
je voudrais soit en forcer une seconde instance (sans avoir à créer un nouveau profil, le programme vba est sensé pouvoir tourner sur n'importe quelle machine), soit récupérer le numéro de process de la fenêtre édition de message (mais j'ai un doute, le gestionnaire des taches de windows ne me le montre même pas ...)

zauriez une idée ?

merci d'avance
 

david84

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

Bonjour,
je ne peux tester car je n'ai pas Thunderbird. Je ne peux donc te donner que des pistes de travail.
Plutôt que d'essayer de récupérer le process, pourquoi ne pas simplement tester si Thunderbird est ouvert ?
Pour cela utiliser l'API FindWindow (fais une recherche pour voir comment l'utiliser).
L'idée est la suivante :
1) soit FindWindow te ramène le Handle (identifiant) de la fenêtre de Thunderbird et donc cela veut dire que Thunderbird est ouvert
2) soit il ramène 0 ce qui veut dire que la fenêtre n'est pas trouvée.

Si la valeur est de 0 l'utilisation de Shell (ou ShellExecute) ouvre une session.
Dans le cas contraire pas besoin d'utiliser Shell.
A+
 

job75

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

Bonjour corion, hello David,

FindWindow renvoie le handle d'une fenêtre, pas d'une application.

Par exemple chez moi sur Mozilla Firefox, le handle du Forum Excel d'XLD :

Code:
Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Sub HandleMozilla()
Dim h As Long
h = FindWindow(vbNullString, "Forum Excel - Mozilla Firefox")
MsgBox h 'pour tester
End Sub
Il n'est pas toujours évident de connaître le nom d'une fenêtre, pour l'obtenir on peut lister toutes les fenêtres ouvertes.

Le code avec les fonctions copiées sur le web :

Code:
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Public Declare Function EnumWindows Lib "user32" _
(ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Public stGetWindowsList As String

Sub Lister()
[A1:A500] = "=INDEX(GetWindowsList(),ROW())"
End Sub

Public Function GetWindowsList()
' Cette fonction retourne sous forme de tableau l'ensemble
' des titres des fenêtres de premier niveau ouvertes sous windows.
Dim lgRep As Long
stGetWindowsList = vbNullString
' Appel de l'API et envoi du pointeur vers notre fonction de rappel
lgRep = EnumWindows(AddressOf EnumWindowsProc, 0)
' Découpage de la chaîne pour retourner un tableau
GetWindowsList = Split(stGetWindowsList, vbCrLf)
End Function

Public Function EnumWindowsProc(ByVal lgHwnd As Long, ByVal lgParam As Long) As Long
Dim stTmp As String, lgTmp As Long, lgRet As Long
stTmp = Space$(120)
lgTmp = 119
' On récupère le titre de la fenêtre à partir du handle
lgRet = GetWindowText(lgHwnd, stTmp, lgTmp)
stTmp = Replace(stTmp, Chr$(0), vbNullString)
' Stockage du résultat dans la chaine temporaire (ajout au texte existant).
' On pourrait imaginer construire une chaîne plus complexe en ajoutant également le handle
' de la fenêtre, ce qui permettrait des manipulations externes (comme une fermeture par exemple).
If (Trim$(stTmp) <> vbNullString) Then stGetWindowsList = stGetWindowsList & stTmp & vbCrLf
' Retourne 1 systématiquement
EnumWindowsProc = 1
End Function
A+
 
Dernière édition:

corion

XLDnaute Nouveau
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

bonjour david84 et job75,
merci de vous pencher sur mon petit pb,

david84:
au niveau process, il n'y a qu'un seul process thunderbird, que je sois en mode rédaction ou non, d'après le gestionnaire de taches ou via cette macro trouvée sur le net (c'est homogène, c'est déjà ça ...) :

Set Reseau = CreateObject("WScript.Network")
Ordinateur = LCase(Reseau.ComputerName)
Set objWsProcess = GetObject("winmgmts:{impersonationLevel=impersonate}!//" & Ordinateur).InstancesOf("Win32_Process")
For Each objProc In objWsProcess
debug.print objProc.Name & " - " & objProc.Caption
Next
---------------------------
job75:
j'ai essayé ton code, en affichant aussi le handle de la fenêtre ;
une fenêtre édition est bien visible, toujours présente que je sois en mode édition ou non :
- j'ai lancé la macro avec juste thunderbird lancé (dossier réception) : la macro indique une fenêtre "Rédaction : (pas de sujet)", que je ne vois pas, avec un handle donné
- je commence à rédiger un nouveau message, sujet "toto" : la macro m'indique la fenêtre "Rédaction : toto", avec le même handle
- je quitte la rédaction : je retrouve la fenêtre "Rédaction : (pas de sujet)", avec toujours le même handle .
cette fenêtre est donc toujours présente, je ne peux pas détecter sa fermeture, ni son changement de handle ;
peut être une option pour savoir si elle est visible ou non ?
je vais creuser encore un peu ...
 

david84

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

Bonjour, salut Gérard,
bon en passant par WMI pourquoi pas mais maintenant que tu sais comment repérer si Thunderbird est ouvert ou non que veux-tu faire maintenant ? C'est quoi le but de la manœuvre ?
A+
 

corion

XLDnaute Nouveau
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

le but n'était pas de savoir si thunderbird était ouvert, mais si la fenêtre de rédaction était fermée ;
il s'avère qu'elle ne l'est jamais, mais qu'elle est juste cachée ;
j'ai fini par trouver que la fenêtre était à un état "non visible", je peux donc me servir de ça ...

Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As Long

merci à vous deux, vous m'avez donné des indices qui m'ont aiguillé vers la solution

maintenant, comment indique-t-on sur ce forum que le sujet est clos ?
 

MJ13

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

Bonjour à tous

Corion: Pour finaliser, normalement, on donne la solution qu'on a trouver, dés fois que quelqu'un soit intéressé par cette discussion :).

Surtout, que c'est quand même un sujet assez complexe.
 

corion

XLDnaute Nouveau
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

voici donc le code pour attendre que la fenêtre de rédaction de thunderbird soit fermée
(mais ça marche pour tout autre fenêtre, avec un peu d'adaptation)

Option Explicit

' déclarations :
'--------------------------------------------------------
'Cette fonction permet de savoir si une fenêtre est visible ou non, 'même cachée par d'autres. Elle retourne 'Vrai' (1) si la fenêtre est visible, sinon 'Faux' (0).
Public Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As Long

'fonction donnant le nom de la fenetre dont on passe le handle en param
' lpString : nom retourné, cch : longueur de la chaine
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

'Cette fonction renvoi le handle de le fenêtre située avant ou après la 'fenêtre dont on passe le handle en paramètre
'wFlag = 2 pour la suivante
'wFlag = 3 pour la précédente
Public Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As Long, ByVal wFlag As Long) As Long

'fonction donnant le handle de la fenêtre active
Public Declare Function GetForegroundWindow Lib "user32" () As Long
'
'--------------------------------------------------------
Function RechercheHandle(Libellé As String, Optional Exact As Boolean = False) As Long
' retroune le handle de la fenêtre dont le libellé est donné
' si Exact = true : Libellé = NomFenêtre
' si Exact = false : Libellé est compris dans le Nom de la fenêtre
Dim Résultat As Long
Dim HandleDépart As Long
Dim Handle As Long
Dim strNom As String
Dim Sens As Byte
Dim x As Long

If Libellé <> "" Then
'le handle de départ est celui de la fenêtre active
HandleDépart = GetForegroundWindow
Handle = HandleDépart

For Sens = 2 To 3 ' suis pas sur que ça serve à qqchose de balayer dans les deux sens, faut que je teste ...
Do
strNom = Space$(120)
' lecture du nom de la fenêtre
x = GetWindowText(hwnd:=Handle, lpString:=strNom, cch:=120)
strNom = LCase(Trim(strNom))

If Exact Then
If strNom = Libellé Then 'libellé exact trouvé
Résultat = Handle
GoTo Fin
End If
Else
If InStr(strNom, Libellé) <> 0 Then 'libellé trouvé dans une partie du nom de fenêtre
Résultat = Handle
GoTo Fin
End If
End If

Handle = GetNextWindow(Handle, Sens)
Loop Until (Handle = HandleDépart) Or (Handle = 0)
Next Sens
End If

Fin:
RechercheHandle = Résultat
End Function
' --------------------------------------------------------
Sub AttenteEnvoiMsg()
' attend que la fenêtre de rédaction de msg soit fermée
' ne fonctionne qu'avec une fenêtre dont le titre comprend "Rédaction" ...(comme thunderbird)
' la fenêtre de rédaction n'est jamais fermée, elle garde toujours le même Handle (tant qu'on ne ferme pas thunderbird complètement), elle est juste cachée
' on teste donc la "visibilité" de cette fenêtre
Dim Handle As Long

' recherche handle de la fenêtre dont le titre comprend le mot "Rédaction" :
Handle = RechercheHandle("rédaction", False)
Debug.Print "Handle: " & Handle

'bouclage jusqu'à ce que la fenêtre soit cachée :
If Handle <> 0 Then
Do
Loop Until IsWindowVisible(Handle) = 0
End If
Debug.Print "Fenêtre rédaction fermée"
End Sub
 

david84

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

Pour l'utilisation de IsWindowVisible et de GetWindowText OK

Après j'ai l'impression que tu pourrais simplifier :
Pourquoi devoir utiliser GetForegroundWindow ? Es-tu obligé de placer la fenêtre au 1er plan pour devoir agir sur elle ?
Quel est le but ? Atteindre la fenêtre de ThunderBird ou sa fenêtre de rédaction ? Si c'est le cas l'utilisation combinée de FindWindow et de FindWindowEx n'est-elle pas plus directe ?

Quelle est l'utilité de la Sub AttenteEnvoiMsg ? Autrement dit pourquoi devoir attendre que la fenêtre soit cachée ?

Sinon je vois que finalement tu n'utilises plus WMI.
Le code de ton message 5 pouvait être également écrit comme cela (écrit pour notepad mais adaptable je pense à Thunderbird) :
Code:
Sub test()
Ordinateur = environ("computername")
Set objWsProcess = GetObject("winmgmts:\\" & Ordinateur).InstancesOf("Win32_Process where Name='notepad.exe'")
If objWsProcess.Count > 0 Then MsgBox "notepad est ouvert"
End Sub
A+
 

corion

XLDnaute Nouveau
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

le GetForegroundWindow me donne le handle de la fenêtre active, à partir de laquelle je balaye les suivantes (et précédentes) avec GetNextWindow ; (il y a peut être une autre solution, faut que j'approfondisse)

je ne connaissais pas l'utilisation de ce "where name= ..., je vais aussi creuser un peu par là

en fait, je veux savoir si la fenêtre rédaction est ouverte ou non, pas thunderbird en général, et il n'y a qu'un seul process pour les deux ;
le fait de fermer la fenêtre de rédaction ne ferme pas thunderbird, et je peux donc attendre longtemps avant de reprendre la main ;
il est vrai qu'avec ma solution, si plusieurs fenêtres de rédaction sont ouvertes, il me faudra attendre la fermeture de toute avant de reprendre la main, mais c'est un cas peu fréquent (je vais d'ailleurs afficher un msgbox (ou un userform) qui indiquera l'attente de fermeture, et que j'effacerai en fin d'attente) (hou là, ma phrase est un rien compliquée ...);
c'est une particularité de thunderbird qui n'existe pas avec notepad;

un truc qui n'a rien à voir :
comment spécifier une fenêtre code, dans les réponses de ce forum ? (la fenêtre en fond bleue)
 

david84

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

A ce moment-là tu peux peut-être utiliser :
- FindWindow pour récupérer le Handle de Thunderbird
- EnumChildWindows pour lister l'ensemble des fenêtres enfants de Thunderbird (en considérant bien entendu que les fenêtres de rédaction sont bien des fenêtres enfants de Thunderbird) pour savoir si une ou plusieurs fenêtres de rédaction sont ouvertes.
Tu sauras ainsi combien de fenêtres de rédactions sont ouvertes.
NB : si toutefois ces fenêtres de rédaction ne sont pas des fenêtres enfants tu peux récupérer le Handle du bureau pour faire tourner EnumChildWindows en précisant dans la recherche que le titre de la fenêtre à lister doit comprendre la chaîne "Rédaction".

En fait EnumChildWindows joue le même rôle que EnumWindows si ce n'est qu'elle est plus axée sur les fenêtres enfant (ce qui peut être intéressant...ou pas selon les cas).

Un exemple ci-joint en recherchant les fenêtres enfant à partir du bureau (testé sur notepad donc à adapter)

Code:
Option Explicit

#If Win64 Then
  Declare PtrSafe Function GetDesktopWindow Lib "user32" () As Long
  Declare PtrSafe Function EnumChildWindows Lib "user32" _
  (ByVal hWndParent As Long, ByVal lpEnumFunc As LongPtr, ByVal lParam As Long) As Long
  Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
  (ByVal Hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
  Declare PtrSafe Function GetWindowTextLength Lib "user32" _
  Alias "GetWindowTextLengthA" (ByVal Hwnd As Long) As Long
  Declare PtrSafe Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _
  ByVal lpClassName As String, ByVal lpWindowName As String) As Long
#Else
  Declare Function GetDesktopWindow Lib "user32" () As Long
  Declare Function EnumChildWindows Lib "user32" _
  (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam 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 GetWindowTextLength Lib "user32" _
  Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
  Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _
  ByVal lpClassName As String, ByVal lpWindowName As String) As Long
#End If


Dim T_Windows(), i As Long


Function EnumChildProc(ByVal Hwnd As Long, ByVal lParam As Long) As Long
  Dim sSave As String
  'Get the windowtext length
  sSave = Space$(GetWindowTextLength(Hwnd) + 1)
  'get the window text
  GetWindowText Hwnd, sSave, Len(sSave)
  'remove the last Chr$(0)
  sSave = Left$(sSave, Len(sSave) - 1)
  
  'If sSave <> "" Then'si l'on veut récupérer toutes les fenêtres sans distinction
  If sSave <> "" And sSave Like "*- Bloc-notes" Then 'si l'on veut récupérer les fenêtres comportant le titre Bloc-notes
    ReDim Preserve T_Windows(i)
    T_Windows(i) = sSave
    i = i + 1
  End If
  'continue enumeration
  EnumChildProc = 1
End Function


Sub Lister_fenetres_enfant()
  Columns(1).Clear
  Erase T_Windows: i = 0
  EnumChildWindows GetDesktopWindow, AddressOf EnumChildProc, ByVal 0&
  If i > 0 Then Cells(1, 1).Resize(i) = Application.Transpose(T_Windows)
End Sub
A+
 

david84

XLDnaute Barbatruc
Re : VBA : Détecter fermeture de la fenêtre d'édition de Thunderbird

A ce moment-là tu peux peut-être utiliser :
- FindWindow pour récupérer le Handle de Thunderbird
- EnumChildWindows pour lister l'ensemble des fenêtres enfants de Thunderbird (en considérant bien entendu que les fenêtres de rédaction sont bien des fenêtres enfants de Thunderbird) pour savoir si une ou plusieurs fenêtres de rédaction sont ouvertes.
Tu sauras ainsi combien de fenêtres de rédactions sont ouvertes.
NB : si toutefois ces fenêtres de rédaction ne sont pas des fenêtres enfants tu peux récupérer le Handle du bureau pour faire tourner EnumChildWindows en précisant dans la recherche que le titre de la fenêtre à lister doit comprendre la chaîne "Rédaction".

En fait EnumChildWindows joue le même rôle que EnumWindows si ce n'est qu'elle est plus axée sur les fenêtres enfant (ce qui peut être intéressant...ou pas selon les cas).

Un exemple ci-joint en recherchant les fenêtres enfant à partir du bureau (testé sur notepad donc à adapter)

Code:
Option Explicit

#If Win64 Then
  Declare PtrSafe Function GetDesktopWindow Lib "user32" () As Long
  Declare PtrSafe Function EnumChildWindows Lib "user32" _
  (ByVal hWndParent As Long, ByVal lpEnumFunc As LongPtr, ByVal lParam As Long) As Long
  Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
  (ByVal Hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
  Declare PtrSafe Function GetWindowTextLength Lib "user32" _
  Alias "GetWindowTextLengthA" (ByVal Hwnd As Long) As Long
#Else
  Declare Function GetDesktopWindow Lib "user32" () As Long
  Declare Function EnumChildWindows Lib "user32" _
  (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam 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 GetWindowTextLength Lib "user32" _
  Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
#End If


Dim T_Windows(), i As Long


Function EnumChildProc(ByVal Hwnd As Long, ByVal lParam As Long) As Long
  Dim sSave As String
  'Get the windowtext length
  sSave = Space$(GetWindowTextLength(Hwnd) + 1)
  'get the window text
  GetWindowText Hwnd, sSave, Len(sSave)
  'remove the last Chr$(0)
  sSave = Left$(sSave, Len(sSave) - 1)
  
  'If sSave <> "" Then'si l'on veut récupérer toutes les fenêtres sans distinction
  If sSave <> "" And sSave Like "*- Bloc-notes" Then 'si l'on veut récupérer les fenêtres comportant le titre Bloc-notes
    ReDim Preserve T_Windows(i)
    T_Windows(i) = sSave
    i = i + 1
  End If
  'continue enumeration
  EnumChildProc = 1
End Function


Sub Lister_fenetres_enfant()
  Columns(1).Clear
  Erase T_Windows: i = 0
  EnumChildWindows GetDesktopWindow, AddressOf EnumChildProc, ByVal 0&
  If i > 0 Then Cells(1, 1).Resize(i) = Application.Transpose(T_Windows)
End Sub
A+

NB :
un truc qui n'a rien à voir :
comment spécifier une fenêtre code, dans les réponses de ce forum ? (la fenêtre en fond bleue)

Quand tu réponds à la discussion cliquer sur "Aller en mode avancé"
 
Dernière édition:

Statistiques des forums

Discussions
312 269
Messages
2 086 674
Membres
103 366
dernier inscrit
SkippyB94