OVH Cloud OVH Cloud

Fermeture fenêtre et fenêtres filles

7 réponses
Avatar
scraper
bonjour/soir à tous ...

y a t il une possibilité pour, lorsqu'on ferme programmatiquement une
fenêtre qui n'a pas été créée par le projet en cours, de fermer aussi ses
fenêtres filles ? (toutes ...)

par exemple, Outlook Express :-)

la première partie fonctionne, j'arrive bien à détecter OE et à le faire
fermer (respectueusement, avec WM_CLOSE et bien entendu, après avoir averti
l'utilisateur)

mais je souhaiterais maintenant aller plus loin et pouvoir fermer ses
fenêtres filles (respectueusement aussi, c'est à dire par exemple, si une
fenêtre de composition de message est ouverte, proposer son enregistrement,
etc .... exactement comme si l'utilisateur fermait lui même ces fenêtres

merci d'avance



--

Attention ! Adresse mail invalide ...
Pour me contacter, cliquez sur le lien ci-dessous:
http://scraper.chez.tiscali.fr/contact.htm

scraper

7 réponses

Avatar
François Picalausa
Hello,

Tout d'abord, l'utilitaire Spy++ fournit avec le platform SDK, Visual
studio, ... permet de mieux comprendre quelles sont les propriétés des
fenêtres avec lesquelles on souhaite interagir. Ceci permet de connaitre les
informations de base. Il est possible d'obtenir encore plus d'informations
avec Inspect (dossier bin du platform SDK).

Pour récupérer toutes les fenêtre liées à outlook express, on peut effectuer
une énumération sur les fenêtres attenantes au thread correspondant. A
partir d'une fenêtre connue, l'API GetWindowThreadProcessId permettra de
retrouver le ThreadID. Ensuite la fonction d'énumération EnumThreadWindows
prendra le relais.
Le message WM_CLOSE est celui qui permettra à l'application de demander
l'avis de l'utilisateur de sauvegarder.

L'exemple suivant retrouve le thread a partir de la classe de fenêtre qu'on
lui passe (par défaut la fenêtre principale d'OE), au travers de FindWindow
et GetWindowThreadProcessId.
Ensuite l'énumération est lancée et la listbox remplie par toutes les
fenêtre du thread portant un titre (celles sans titre sont ignorées dans cet
exemple).
Un click sur le second boutton de commande permet alors de fermer la fenêtre
sélectionnée dans la listbox

'Dans une form (Form1) contenant 2 commandbuttons (Command1, Command2), une
listbox (List1) et une textbox (Text1):
Option Explicit

Private Const WM_CLOSE = &H10

Private Declare Function GetWindowThreadProcessId _
Lib "user32" _
( _
ByVal hwnd As Long, _
lpdwProcessId As Long _
) _
As Long
Private Declare Function EnumThreadWindows _
Lib "user32" _
( _
ByVal dwThreadId As Long, _
ByVal lpfn As Long, _
lParam As Any _
) _
As Long
Private Declare Function GetWindowText _
Lib "user32" _
Alias "GetWindowTextA" _
( _
ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal nMaxCount As Long _
) _
As Long
Private Declare Function GetWindowTextLength _
Lib "user32" _
Alias "GetWindowTextLengthA" _
( _
ByVal hwnd As Long _
) _
As Long
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) _
As Long
Private Declare Function SendMessage _
Lib "user32" _
Alias "SendMessageA" _
( _
ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any _
) _
As Long

Private Sub Command1_Click()
Dim hwnd As Long, WindowThread As Long
hwnd = FindWindow(Text1.Text, vbNullString)
WindowThread = GetWindowThreadProcessId(hwnd, ByVal 0&)

'Vide la liste avant énumération
List1.Clear

'Enumération des fenêtres du thread
'Attention, AddressOf requiert un module standard!
EnumThreadWindows WindowThread, AddressOf EnumThreadWndProc, ByVal 0&
End Sub

Private Sub Command2_Click()
'List1.ListIndex est l'élément sélectionné
'List1.ItemData(List1.ListIndex), la donnée associée à l'élément
sélection, en l'occurence l'hwnd
SendMessage List1.ItemData(List1.ListIndex), WM_CLOSE, ByVal 0&, ByVal
0&
End Sub

Private Sub Form_Load()
Text1.Text = "Outlook Express Browser Class"
Command1.Caption = "Rechercher"
Command2.Caption = "Send Close"
End Sub

Public Function EnumWindowsCallback(ByVal hwnd As Long, ByVal lParam As
Long) As Long
'Cette procédure est appelée dans la form en réponse au callback dans le
module
Dim t_nTitleCount As Long
Dim t_szTitle As String

t_nTitleCount = GetWindowTextLength(hwnd)

'Si un titre est présent
If t_nTitleCount Then
'Détermine le titre
t_szTitle = String$(t_nTitleCount, vbNullChar)
GetWindowText hwnd, t_szTitle, t_nTitleCount + 1

'Et ajoute l'item dans la liste
List1.AddItem t_szTitle
List1.ItemData(List1.NewIndex) = hwnd 'Stoque la fenêtre
correspondante!
End If

EnumWindowsCallback = 1
End Function

'Dans un module standard:
Option Explicit

Public Function EnumThreadWndProc(ByVal hwnd As Long, ByVal lParam As Long)
As Long
'Appelle la procédure de traitement réelle, dans la form
EnumThreadWndProc = Form1.EnumWindowsCallback(hwnd, lParam)
End Function

Pour connaitre les fenêtres "réelles", il faut effectuer un filtrage plus
intelligent qu'un filtrage sur le filtre.
On peut faire cela par exemple en testant si le style WS_VISIBLE appartient
à la fenêtre:
<déclarations d'API correspondantes />
Dim Style As Long
Style = GetWindowLong(Me.hWnd, GWL_STYLE)
If Style And WS_VISIBLE Then
'La fenêtre est visible!
End If

Le fait que GetParent ne renvoie aucune fenêtre peut être une autre
indication
le style WS_OVERLAPPEDWINDOW peut être une très bonne indication...
Bien entendu, cela varie pour chaque application, donc ces conseils plus ou
moins généraux sont à tester et à adapter au cas spécifique.

<info version shell>
Pour savoir si une fenêtre d'outlook express est ouverte, il est possible
d'énumérer les fenêtres présentes sur la barre de tâches, soit par filtrage
sur les styles en question, soit en utilisant les interfaces COM appropriée.
La parcours de la barre des tâches de cette manière ne prendra *pas* en
compte les boites de dialogue d'option. Toutefois cette alternative peut
être plus conviviale dans certains cas particuliers. Cela pourrait aussi
aider à déterminer le thread concernant les fenêtres d'OE. En supposant que
quand OE est ouvert, il y a toujours au moins une fenêtre présente sur la
barre des tâches, on peut parcourir l'ensemble des fenêtres et comparer par
rapport au nom du process (on peut récupérer le processid à partir de
GetWindowThreadProcessId) et donc trouver le thread... à condition que
l'utilisateur n'ait pas renommé l'exécutable.
</info>

Afin de savoir si une fenêtre d'outlook express existe réellement, voici une
autre solution
Finalement, il est possible d'énumérer toutes les fenêtres de premier plan
via EnumWindows
Private Declare Function EnumWindows _
Lib "user32" _
( _
ByVal lpEnumFunc As Long, _
lParam As Any _
) _
As Long
Public Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As
Long

En récupérant la classe de la fenêtre énumérée (GetClassName) et en
comparant cette classe à quelques valeurs prédéfinies, on peut déterminer,
avec une bonne précision, une fenêtre d'outlook express.

J'espère ne pas avoir été trop long, trop sybillin, trop étallant - comme la
confiture ;-)

--
François Picalausa

"scraper" a écrit dans le message de news:

bonjour/soir à tous ...

y a t il une possibilité pour, lorsqu'on ferme programmatiquement une
fenêtre qui n'a pas été créée par le projet en cours, de fermer
aussi ses fenêtres filles ? (toutes ...)

par exemple, Outlook Express :-)

la première partie fonctionne, j'arrive bien à détecter OE et à le
faire fermer (respectueusement, avec WM_CLOSE et bien entendu, après
avoir averti l'utilisateur)

mais je souhaiterais maintenant aller plus loin et pouvoir fermer ses
fenêtres filles (respectueusement aussi, c'est à dire par exemple,
si une fenêtre de composition de message est ouverte, proposer son
enregistrement, etc .... exactement comme si l'utilisateur fermait
lui même ces fenêtres
merci d'avance


Avatar
scraper
Bonjour François Picalausa, dans le message
news:%
tu disais :

[...]

L'exemple suivant retrouve le thread a partir de la classe de fenêtre
qu'on lui passe (par défaut la fenêtre principale d'OE), au travers
de FindWindow et GetWindowThreadProcessId.
Ensuite l'énumération est lancée et la listbox remplie par toutes les
fenêtre du thread portant un titre (celles sans titre sont ignorées
dans cet exemple).
Un click sur le second boutton de commande permet alors de fermer la
fenêtre sélectionnée dans la listbox



[...]

OK ... bon, j'utilise déjà quelques unes de ces fonctions pour détecter OE
...

là, je suis impressionné .... :-)

ça fonctionne super bien ;-)
(je pensais même pas qu'il y avait autant de fenêtres liées à OE lol

mais j'ai tout de même un problème ! (pas lié au code ;-)

certaines des fenêtres que je veux fermer ne se ferment pas par ce code ?
notament une fenêtre de composition (ou de réponse ) de message ...

or, sil semble que, même lors de la fermeture de OE, si une telle fenêtre
est ouverte, OE lui même n'en demande pas la fermeture !

il me semble donc que je suis coincé ....
tant pis, je vais revoir mon code et l'adapter à cet impératif

merci en tout cas pour ce bout de code (que je garde bien préciseusement hé


J'espère ne pas avoir été trop long, trop sybillin, trop étallant -
comme la confiture ;-)



nan nan :-)

en plus, pour une fois j'ai (presque) tout compris lol




merci



--

Attention ! Adresse mail invalide ...
Pour me contacter, cliquez sur le lien ci-dessous:
http://scraper.chez.tiscali.fr/contact.htm

scraper
Avatar
François Picalausa
"scraper" a écrit dans le message de news:

Bonjour François Picalausa, dans le message
news:%
tu disais :

[...]

L'exemple suivant retrouve le thread a partir de la classe de fenêtre
qu'on lui passe (par défaut la fenêtre principale d'OE), au travers
de FindWindow et GetWindowThreadProcessId.
Ensuite l'énumération est lancée et la listbox remplie par toutes les
fenêtre du thread portant un titre (celles sans titre sont ignorées
dans cet exemple).
Un click sur le second boutton de commande permet alors de fermer la
fenêtre sélectionnée dans la listbox


certaines des fenêtres que je veux fermer ne se ferment pas par ce
code ? notament une fenêtre de composition (ou de réponse ) de
message ...



Hello,

Ici, avec le bout de code suivant, toutes les fenêtres sans exception sont
fermées et le process est terminé...
Je ne suis pas sûr de bien comprendre où se situe le problème? peut-être
dans la fenêtre de confirmation d'enregistrement des modifications?

Option Explicit

Private Const WM_CLOSE = &H10

Private Declare Function GetWindowThreadProcessId _
Lib "user32" _
( _
ByVal hWnd As Long, _
lpdwProcessId As Long _
) _
As Long
Private Declare Function EnumThreadWindows _
Lib "user32" _
( _
ByVal dwThreadId As Long, _
ByVal lpfn As Long, _
lParam As Any _
) _
As Long
Private Declare Function EnumWindows _
Lib "user32" _
( _
ByVal lpfn As Long, _
lParam As Any _
) _
As Long
Private Declare Function GetWindowText _
Lib "user32" _
Alias "GetWindowTextA" _
( _
ByVal hWnd As Long, _
ByVal lpString As String, _
ByVal nMaxCount As Long _
) _
As Long
Private Declare Function GetWindowTextLength _
Lib "user32" _
Alias "GetWindowTextLengthA" _
( _
ByVal hWnd As Long _
) _
As Long
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) _
As Long
Private Declare Function SendMessage _
Lib "user32" _
Alias "SendMessageA" _
( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any _
) _
As Long
Private Declare Function IsWindowVisible _
Lib "user32" _
( _
ByVal hWnd As Long _
) _
As Long
Private Declare Function GetParent _
Lib "user32" _
( _
ByVal hWnd As Long _
) _
As Long
Private Declare Function PostMessage _
Lib "user32" _
Alias "PostMessageA" _
( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long _
) _
As Long
Private Declare Function GetClassName _
Lib "user32" _
Alias "GetClassNameA" _
( _
ByVal hWnd As Long, _
ByVal lpClassName As String, _
ByVal nMaxCount As Long _
) _
As Long

Private m_OEWnd As Long

Private Sub Command1_Click()
Dim WindowThread As Long, Async As Long

EnumWindows AddressOf EnumWndProc, ByVal 0&

Async = 1

WindowThread = GetWindowThreadProcessId(m_OEWnd, ByVal 0&)
EnumThreadWindows WindowThread, AddressOf EnumThreadWndProc, ByVal Async
End Sub

Private Sub Form_Load()
Command1.Caption = "Fermer OE"
End Sub

Public Function EnumThreadWindowsCallback(ByVal hWnd As Long, ByVal lParam
As Long) As Long
Dim t_nTitleCount As Long
Dim t_szTitle As String

t_nTitleCount = GetWindowTextLength(hWnd)
'Si un titre est présent
If t_nTitleCount > 0 And GetParent(hWnd) = 0 And IsWindowVisible(hWnd)
Then
'Détermine le titre
t_szTitle = String$(t_nTitleCount, vbNullChar)
GetWindowText hWnd, t_szTitle, t_nTitleCount + 1

'Et l'affiche
If lParam Then
PostMessage hWnd, WM_CLOSE, ByVal 0&, ByVal 0&
Else
SendMessage hWnd, WM_CLOSE, ByVal 0&, ByVal 0&
End If
End If

EnumThreadWindowsCallback = 1
End Function

Public Function EnumWindowsCallback(ByVal hWnd As Long, ByVal lParam As
Long) As Long
Dim t_szWndClass As String

EnumWindowsCallback = 1

t_szWndClass = GetWindowClass(hWnd)

'Fenêtre de OE?
If t_szWndClass = "Outlook Express Browser Class" Or _
t_szWndClass = "ATH_Note" Or _
t_szWndClass = "OutlookExpressHiddenWindow" Or _
t_szWndClass = "Outlook Express Browser Class" Then

'La stoque pour énumérer toutes les autres fenêtres de OE
m_OEWnd = hWnd

EnumWindowsCallback = 0
End If
End Function

Public Function GetWindowClass(hWnd As Long) As String
Dim lngBufferSize As Long, RC As Long
Const BUFFER_CHUNK_SIZE = 255

Do
lngBufferSize = lngBufferSize + BUFFER_CHUNK_SIZE
GetWindowClass = String$(lngBufferSize, vbNullChar)
RC = GetClassName(hWnd, GetWindowClass, lngBufferSize + 1)
Loop Until RC < lngBufferSize

GetWindowClass = Left$(GetWindowClass, RC)
End Function

--
François Picalausa
Avatar
scraper
Bonjour François Picalausa, dans le message
news:
tu disais :

Hello,

Ici, avec le bout de code suivant, toutes les fenêtres sans exception
sont fermées et le process est terminé...
Je ne suis pas sûr de bien comprendre où se situe le problème?
peut-être dans la fenêtre de confirmation d'enregistrement des
modifications?



ben là, avec ce code ci, ça fonctionne ...

j'ai rajouté ça, dans un module ??

Public Function EnumThreadWndProc(ByVal hWnd As Long, _
ByVal lParam As Long) As Long

EnumThreadWndProc = Form1.EnumThreadWindowsCallback(hWnd, lParam)

End Function

Public Function EnumWndProc(ByVal hWnd As Long, _
ByVal lParam As Long) As Long

EnumWndProc = Form1.EnumWindowsCallback(hWnd, lParam)

End Function




et de fait, ça fonctionne ....
si OE est ouvert !
parce que sinon, plantage garanti ....

je suis pas certain que ce ne soit pas à cause des fc que j'ai rajoutées ??

mais là, je dois t'avouer très honnêtement que je suis largué dans ces
callback, handle, et autres threadId :-(

bouhhh .... c'est dur le VB :-(

merci en tout cas de ton aide



--

Adresse invalide
Merci de me répondre sur le Forum ...
mon site : http://scraper.chez.tiscali.fr

scraper
Avatar
François Picalausa
Hello,

"scraper" a écrit dans le message de news:

Bonjour François Picalausa, dans le message
news:
Hello,

Ici, avec le bout de code suivant, toutes les fenêtres sans exception
sont fermées et le process est terminé...
Je ne suis pas sûr de bien comprendre où se situe le problème?
peut-être dans la fenêtre de confirmation d'enregistrement des
modifications?



ben là, avec ce code ci, ça fonctionne ...



C'est ce que je croyais... j'avais pas été ci clair dans mes explications
précédentes :-)

j'ai rajouté ça, dans un module ??

Public Function EnumThreadWndProc(ByVal hWnd As Long, _
ByVal lParam As Long) As Long

EnumThreadWndProc = Form1.EnumThreadWindowsCallback(hWnd, lParam)

End Function

Public Function EnumWndProc(ByVal hWnd As Long, _
ByVal lParam As Long) As Long

EnumWndProc = Form1.EnumWindowsCallback(hWnd, lParam)

End Function




Exact... les joies du copier-coller...

et de fait, ça fonctionne ....
si OE est ouvert !
parce que sinon, plantage garanti ....



Analysons ce qui se passe:
Dans un premier temps,
EnumWindows AddressOf EnumWndProc, ByVal 0&
tente de retrouver une fenêtre de OE. Si une fenêtre est trouvée, son handle
est stoqué dans
m_OEWnd

Cependant, quel que soit le résultat, on énumère les fenêtres du thread...
il faudrait ajouter un test du style
If OEWnd Then

Ensuite seulement on peut tenter de récupérer le thread qui a créé toutes
les fenêtres d'OE
WindowThread = GetWindowThreadProcessId(m_OEWnd, ByVal 0&)
Pour énumérer les fenêtres créées par ce thread
EnumThreadWindows WindowThread, AddressOf EnumThreadWndProc, ByVal Async
End If

mais là, je dois t'avouer très honnêtement que je suis largué dans ces
callback, handle, et autres threadId :-(



Essaye peut être de reprendre le code en pas à pas voir comment il tourne?

--
François Picalausa
Avatar
scraper
Bonjour François Picalausa, dans le message
news:
tu disais :

ben là, avec ce code ci, ça fonctionne ...



C'est ce que je croyais... j'avais pas été ci clair dans mes
explications précédentes :-)



ben ... euh .... je croyais avoir compris (à peu près ... )
et pis t'en as rajouté une couche lol

Exact... les joies du copier-coller...



bon ... j'ai pas tout faux ;-)

Analysons ce qui se passe:
Dans un premier temps,
EnumWindows AddressOf EnumWndProc, ByVal 0&
tente de retrouver une fenêtre de OE. Si une fenêtre est trouvée, son
handle est stoqué dans
m_OEWnd

Cependant, quel que soit le résultat, on énumère les fenêtres du
thread... il faudrait ajouter un test du style
If OEWnd Then



ah OK ... là, tu vois, en disant ça, de suite, je capte mieux comment
bloquer le truc :-)

Ensuite seulement on peut tenter de récupérer le thread qui a créé
toutes les fenêtres d'OE
WindowThread = GetWindowThreadProcessId(m_OEWnd, ByVal 0&)
Pour énumérer les fenêtres créées par ce thread
EnumThreadWindows WindowThread, AddressOf EnumThreadWndProc, ByVal
Async End If

mais là, je dois t'avouer très honnêtement que je suis largué dans
ces callback, handle, et autres threadId :-(



Essaye peut être de reprendre le code en pas à pas voir comment il
tourne?



ben j'ai essayé ... :-(

je reprendrai ça à tête reposée demain :-)


mais là, ton code fonctionne le feu :-)

je suppose que la fenêtre de demande d'enregistrement est provoquée par le
SendMessage, au fait ? :-)


merci énormément de ton aide, en tout cas ... :-)

moi, dès qu'on manipule un peu trop les handle je suis à la ramasse :-(



--

Adresse invalide
Merci de me répondre sur le Forum ...
mon site : http://scraper.chez.tiscali.fr

scraper
Avatar
François Picalausa
Hello,

"scraper" a écrit dans le message de news:

Bonjour François Picalausa, dans le message
news:
tu disais :
je suppose que la fenêtre de demande d'enregistrement est provoquée
par le SendMessage, au fait ? :-)



Ce n'est pas l'effet de SendMessage mais du message WM_CLOSE ;-)
<Quote
src="http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowmessages/wm_close.asp">
An application can prompt the user for confirmation, prior to destroying a
window, by processing the WM_CLOSE message and calling the DestroyWindow
function only if the user confirms the choice.
</Quote>

moi, dès qu'on manipule un peu trop les handle je suis à la ramasse :-(



un handle n'est rien d'autre qu'un nombre lié à un "objet" quelconque... si
tu ne vois pas ce que représente le handle, c'est que le nom de la variable
ne signifie rien... dans ce cas, il faut peut-être renommer la variable...

--
François Picalausa