RÉCUPÉRER LE HANDLE APRÈS UN SHELLEXECUTE

BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019 - 9 sept. 2006 à 11:08
cs_didine13 Messages postés 96 Date d'inscription mardi 18 août 2009 Statut Membre Dernière intervention 14 août 2013 - 3 sept. 2010 à 18:09
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/39492-recuperer-le-handle-apres-un-shellexecute

cs_didine13 Messages postés 96 Date d'inscription mardi 18 août 2009 Statut Membre Dernière intervention 14 août 2013
3 sept. 2010 à 18:09
Je vient de tester la source 10/10 bravo
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
5 oct. 2009 à 10:48
le plus simple est d'executer le programme souhaité, non ??

tu peux l'obtenir en regardant

HKEY_CLASSES_ROOT\mailto\shell\open\command
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
5 oct. 2009 à 10:43
Salut - surement parce que "mailto" n'ouvre pas une nouvelle instance de Outlook mais une sous-fenêtre.
Regarde voir cette dernière source http://www.vbfrance.com/codes/DATE-HEURE-LANCEMENT-PROGRAMME_50375.aspx
Tu pourras peut-être retrouver ton bonheur, mais je ne pense pas qu'une fenêtre de nouveau message génère le lancement d'un nouvel EXE ...
franky131 Messages postés 1 Date d'inscription lundi 9 octobre 2006 Statut Membre Dernière intervention 4 octobre 2009
4 oct. 2009 à 23:54
Bonjour Jack. 3 ans après sa publication, ton code fait toujours des heureux, comme moi ! Par contre, je ne comprends pas pourquoi il ne fonctionne pas avec la commande mailto (mailto:dest@domaine.com?Subject=Le sujet&Body=Le message). Outlook Express est bien lancé, mais le handle retourné est égal à zéro... Une idée ?
cs_epasquier Messages postés 9 Date d'inscription dimanche 8 juin 2003 Statut Membre Dernière intervention 2 septembre 2008
2 sept. 2008 à 22:26
Bonjour,
Il faut corriger la ligne "SEI.hProcess > 0" par "SEI.hProcess <> 0" !

Eric.
cs_xeroc Messages postés 3 Date d'inscription vendredi 10 octobre 2003 Statut Membre Dernière intervention 7 avril 2016
10 août 2007 à 11:04
Bonjour,

Je viens de tomber sur ton source et franchement.
Je le trouve bien fait et surtout bien commenté.

Merci à toi
chricha Messages postés 11 Date d'inscription lundi 2 janvier 2006 Statut Membre Dernière intervention 3 novembre 2006
16 sept. 2006 à 22:52
"La seule particularité que j'ai trouvé au thread correspondant à notre fenêtre, c'est
' qu'elle prend le focus au sein du groupe. Il suffit donc de vérifier si le handle
' du thread est le même que la fenêtre qui a le focus dans ce thread"
excellente idée Jack.
et en + tu l'as fait et ça a l'air de bien marcher.
trés bien commenté.
Les débutants comme moi ne vous remercieront jamais assez.10/10

Merci aussi a renfield (THE reference) pour tout ce qu'il apporte.
Ulala2 Messages postés 86 Date d'inscription lundi 27 janvier 2003 Statut Membre Dernière intervention 10 décembre 2006
13 sept. 2006 à 08:02
Bonjour,

changer le nom de la fenêtre n'est pas une mauvaise idée, je vais tester cette solution.

merci pour vos réponses, sur vbfrance on trouve toujours une solution, même alternative :)
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
13 sept. 2006 à 07:52
comparaison de la liste ?
risqué, car les process vont et viennent.

si tu cible une appli en particulier, tu peux toujours utiliser périodiquement un FindWindow (un hook sur tout le systeme, j'y crois moyen, en VB...), modifier le menu, et le titre de l'appli, en ajoutant, par exemple des espaces en fin de nom, afin de ne pas retomber sur cette instnace en particulier...

pas très joli, mais efficace (j'ai deja utilisé ce principe, pour ajouter des boutons, des options, etc dans une vieille appli, au boulot^^)
Ulala2 Messages postés 86 Date d'inscription lundi 27 janvier 2003 Statut Membre Dernière intervention 10 décembre 2006
13 sept. 2006 à 00:24
Brunews, je parviens à desactiver la croix sans problème pour la première fenêtre et j'ajoute un menu dynamiquement via API, que je gère via un hook.

Comme je le disais dans le forum, dans un topic que j'ai ouvert juste avant lolpop, FindWindow fonctionne très bien pour la recherche de la première fenêtre, mais si l'utilisateur lance plusieurs fois le .exe (ce qui est toujours le cas, de 3 à 5 fois en moyenne) alors je veux être certain de récupérer le bon handle pour y désactiver la croix et ajouter un menu.

en ce qui concerne la comparaison avec une ancienne liste, c'est à étudier.

Je vais voir ca.
draluorg Messages postés 625 Date d'inscription vendredi 23 avril 2004 Statut Membre Dernière intervention 25 novembre 2010
12 sept. 2006 à 23:53
Salut,

Eh je pense que y a moyen...

Tu verifie si le process est en cours, si oui tu liste les fenetres active pour ce processus et puis tu lance ton exe et tu reliste les fenetre active pour ce process et tu la trouvera en comparant avec l'ancienne liste...
Mais a mon avis ce sera la derniere donc surement pas besoin de comparer (a verifier...)

++
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
12 sept. 2006 à 22:11
Si elle a un titre précis alors FindWindow() et tu l'as.

Sinon c'est plus délicat (encore que..):
Prepare un hook WH_SHELL avant de lancer cet exe, quand il créera la fenetre tu pourras l'intercepter. Pas très compliqué mais aucune idée comment faire cela en VB (je n'y crois d'ailleurs pas).
Ulala2 Messages postés 86 Date d'inscription lundi 27 janvier 2003 Statut Membre Dernière intervention 10 décembre 2006
12 sept. 2006 à 21:54
Bonjour,

si j'ai bien suivi, il n'est pas possible de récupérer un handle de fenêtre, si le programme qui a lancé cette fenêtre est déjà ouvert.

Je m'explique, j'ai un .exe que je lance. La première fois il s'ouvre pas de problème.
Celui-ci ouvre une form modale via une DLL.

La seconde fois que je lance ce .exe, il ne recréé pas un second processus, mais relance une fenêtre modale via cette même DLL.

Et la je suis embêté, car j'aurais voulu désactiver la croix de ces formulaires.


Si j'ai bien compris, il ne me sera pas possible de récupérer les handles des différentes fenêtres modales ?


Merci pour vos réponses.
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
10 sept. 2006 à 20:16
Voilà la version finale avec ShellExecuteEx (MàJ)
Une partie de plaisir !
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
10 sept. 2006 à 17:10
Oui, je sais que si le programme hôte est déjà ouvert, on n'obtiendra pas de Handle.
Mais, hors mis ce cas particulier, la question reste entière : Comment faire le lien entre le hProcess et le handle de la fenêtre du programme hôte.
Je vais fouiller du côté des lien entre thread (parent/child)
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
10 sept. 2006 à 13:54
pdf est un MDI, si processus déjà ouvert, crée un new document dans l'ancien process.

CreateProcess pour avoir un new process à tout coup.
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
10 sept. 2006 à 13:43
PS : Ne fonctionne pas en lançant un fichier PDF par exemple, Calc.exe étant trop particulier
draluorg Messages postés 625 Date d'inscription vendredi 23 avril 2004 Statut Membre Dernière intervention 25 novembre 2010
10 sept. 2006 à 13:42
re,

Ah ok je viens de comprendre que ce que tu voulais c'etait le handle de la fenetre et non du process...

bienvu renfield ;)

Pour reprendre la petite histroire du hProcess, c'est bien ce que l'on appel handle de processus, renfield vient d'ailleur de le prouver avec sa fonction. D'ailleur si on ferme un hProcess avec un CloseHandle c'est pas pour rien!

++
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
10 sept. 2006 à 13:34
Salut Renfield et merci.
Mais ... ça ne renvoie pas le handle de l'application.
Le Handle retourné est très proche (507A6 pour 507B0), mais n'est pas le bon.
Dans notre cas, le Move ne se fait pas

Ca me rassure quelque part, tes lignes étant presque identiques aux miennes.
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
10 sept. 2006 à 11:34
bien que l'on puisse utiliser ici Shell, qui revoit directement un pid,
nous avons interet a utiliser shellexecuteex, car elle propose davantage d'options
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
10 sept. 2006 à 10:52
Option Explicit

Private Const SEE_MASK_DOENVSUBST As Long = &H200
Private Const SEE_MASK_IDLIST As Long = &H4
Private Const SEE_MASK_NOCLOSEPROCESS As Long = &H40
Private Const SW_HIDE As Long = 0
Private Const SW_SHOW As Long = 5
Private Const WAIT_TIMEOUT As Long = 258&
Private Const GW_HWNDNEXT = 2

Private Type SHELLEXECUTEINFOA
cbSize As Long
fMask As Long
hwnd As Long
lpVerb As String
lpFile As String
lpParameters As String
lpDirectory As String
nShow As Long
hInstApp As Long
lpIDList As Long
lpClass As String
hkeyClass As Long
dwHotKey As Long
hIcon As Long
hProcess As Long
End Type

Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function GetProcessId Lib "kernel32.dll" (ByVal Process As Long) As Long
Private Declare Function ShellExecuteEx Lib "shell32.dll" (ByRef lpExecInfo As SHELLEXECUTEINFOA) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32.dll" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function MoveWindow Lib "user32.dll" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long

Public Function WindowFromPID(ByVal vhPid As Long) As Long
Dim hPid As Long
'# Point de départ de l'énumération
WindowFromPID = FindWindow(0, 0)
Do While WindowFromPID <> 0
'# Si c'est une fenetre de premier niveau
If GetParent(WindowFromPID) = 0 Then
'# On demande le PID auquel elle est associée
GetWindowThreadProcessId WindowFromPID, hPid
'# Et on le compare avec notre PID
If hPid = vhPid Then
Exit Function
End If
End If
'# Fenêtre suivante
WindowFromPID = GetWindow(WindowFromPID, GW_HWNDNEXT)
Loop
WindowFromPID = 0
End Function

Public Function ShellWnd(ByRef vsCmdLine As String, Optional ByRef vsParameters As String, Optional ByRef vsCurrentDirectory As String vbNullString, Optional ByVal vnShowCmd As Long SW_SHOW) As Long
Dim lpShellExInfo As SHELLEXECUTEINFOA
Dim hProcessID As Long
With lpShellExInfo
.cbSize = Len(lpShellExInfo)
.lpDirectory = vsCurrentDirectory
.lpVerb = "open"
.lpFile = vsCmdLine
.lpParameters = vsParameters
.nShow = vnShowCmd
.hInstApp = App.hInstance
.fMask = SEE_MASK_DOENVSUBST Or SEE_MASK_NOCLOSEPROCESS Or SEE_MASK_IDLIST
End With

If ShellExecuteEx(lpShellExInfo) Then
'# On demande le PID en partant du hprocess
hProcessID = GetProcessId(lpShellExInfo.hProcess)

Do
'# On tente de récpérer le handle de la première fenetre de notre processus
ShellWnd = WindowFromPID(hProcessID)
'# On attend qu'une fenêtre ai pu être crée où quel processus soit fermé...
Loop While ShellWnd 0 And (WaitForSingleObject(lpShellExInfo.hProcess, 200) WAIT_TIMEOUT)

CloseHandle lpShellExInfo.hProcess
End If
End Function

Private Sub Form_Load()
Dim hCalcWnd As Long
hCalcWnd = ShellWnd("Calc.exe")
SetParent hCalcWnd, Me.hwnd
MoveWindow hCalcWnd, 0, 0, Me.ScaleX(Me.Width, vbTwips, vbPixels), Me.ScaleY(Me.Height, vbTwips, vbPixels), 1
End Sub
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
10 sept. 2006 à 04:36
Bon, il est tard. J'ai encore passé des heures dessus. Rien à faire. J'abdique.
Le test réussi de toute à l'heure était un heureux hasard.

Si ça tente qqun de se dépétrer avec ce ShellExecuteEx qui, et je l'affirme, ne renvoie pas le handle de la fenêtre générée, mais un handle de Process.
A partir de là, je n'ai rien trouvé pour récupérer le véritable handle (ni ProcessId d'ailleur).

GetProcessId renvoie bien le ProcessId MAIS à condition de lui fournir un handle, un vrai (et SEI.hProcess n'en est pas un)

Pour les courageux :
- à partir d'une instruction (en "open") standard lRet = ShellExecuteEx(SEI)
- à partir du SEI.hProcess, retrouver le Hwnd de la fenêtre qui vient de s'ouvrir
Le Hwnd qui permettra par exemple de s'adresser à la fenêtre pour lui faire un "MoveWindow"

Bon courage.
Si vous trouvez, je vire ma source de suite !
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
9 sept. 2006 à 22:04
jack > la taille de structure est totalement indépendante de ce que contiendront les chaines.
La taille d'un pointeur sur system 32bits est de 4 octets et basta.

Clarifions ces "String":
membre lpVerb par exemple, c'est un nombre de 4 octets et rien d'autre. C'est l'adresse du premier octet d'une série qui finira pas un NULL (octet 0). Voila ce qu'est un paramètre chaine, que vous écrivez String en VB mais VB sait qu'il s'adresse à une dll et il aura donc mis à une adresse la copie de votre "String" avec teminateur NULL et ensuite, lpVerb adresse.
draluorg Messages postés 625 Date d'inscription vendredi 23 avril 2004 Statut Membre Dernière intervention 25 novembre 2010
9 sept. 2006 à 20:49
Eh et c'est quoi alors ?
Si ca renvoi le Pid en faisant un GetProcessId dessus c'est que c'est son handle... non ?

++
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
9 sept. 2006 à 20:44
Pas d'accord avec toi Draluorg : hProcess n'est pas un Handle de fenêtre, mais un handle de Process
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
9 sept. 2006 à 20:38
Juste deux remarques dans vos codes (et je vois ces anomalies partout) :

Dans le SEI, le paramètre cbSize définit la taille de la structure d'échange.
Partout, je lis ceci : .cbSize = Len(lpShellExInfo)
-1- Normalement, c'est LenB qu'il faudrait utiliser
-2- Puisque dans la structure il y a des String, la longueur de la structure n'est pas encore connue avant le renseignement des chaines (de longueurs variables). Donc, cette instruction devrait se retrouver à la fin, juste avant le "End With".

Dans la doc, les String sont des szString, donc terminées par des Chr(0), chose qui est toujours omises dans les exemples et qui est impérative lorsqu'on saisit le nom du fichier directement dans la structure, sans passer par une variable.

--- 1h30 plus tard ---
Grosse galère, ça marche pas à tous les coups. Je n'ai pas encore analyser pourquoi.
Je verrais ça demain.
draluorg Messages postés 625 Date d'inscription vendredi 23 avril 2004 Statut Membre Dernière intervention 25 novembre 2010
9 sept. 2006 à 19:43
Re,

Oui ca revoi bien le Pid, car je pensais qu'au final c'est ce que tu voulais, si tu veux le handle c'est pareil suffit de remplacer:
ShellEx = GetProcessId(lpShellExInfo.hProcess)
par:
ShellEx = lpShellExInfo.hProcess

++
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
9 sept. 2006 à 19:13
Draluorg : Non, ShellEx de ton exemple n'est pas le Handle de l'application, mais son ProcessId

Un éclair de reflexion et de recherche et je viens de m'apercevoir de mon erreur :
(dans la recherche du Handle à partir du vrai ProcessId)

Pour commencer l'énumération, j'utilisais le FindWindow initial comme ceci
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
et lTest_hwnd = FindWindow(ByVal 0, ByVal 0)

qui donne un autre résultat que cette déclaration là
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long
et lTest_hwnd = FindWindow(vbNullString, vbNullString)

La première semble ne fournir que les handles des fenêtres appartenant au projet en cours.

Maintenant, ça fonctionne.
Je mets à jour cette source avec les deux méthodes.

Merci à tous pour vos infos.
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
9 sept. 2006 à 17:04
vi, c'est ce dont je me sers dans ma source ShellAndWait, qui attend la fermeture du process

http://www.vbfrance.com/codes/SHELLANDWAIT-EXECUTER-APPLICATION-ATTENDRE-FIN-RENVOYER-SON-CODE_34867.aspx
draluorg Messages postés 625 Date d'inscription vendredi 23 avril 2004 Statut Membre Dernière intervention 25 novembre 2010
9 sept. 2006 à 15:56
Salut a tous,

D'accord avec BruNews, il faut utiliser ShellExecuteEx

Voici une exemple vite fait:

Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function ShellExecuteEx Lib "shell32.dll" (ByRef lpExecInfo As SHELLEXECUTEINFOA) As Long
Private Declare Function GetProcessId Lib "kernel32" (ByVal hProcessId As Long) As Long

Private Type SHELLEXECUTEINFOA
cbSize As Long
fMask As Long
hwnd As Long
lpVerb As String
lpFile As String
lpParameters As String
lpDirectory As String
nShow As Long
hInstApp As Long
lpIDList As Long
lpClass As String
hkeyClass As Long
dwHotKey As Long
hIcon As Long
hProcess As Long
End Type

Private Const SEE_MASK_DOENVSUBST As Long = &H200
Private Const SEE_MASK_IDLIST As Long = &H4
Private Const SEE_MASK_NOCLOSEPROCESS As Long = &H40

Public Function ShellEx(ByRef vsCmdLine As String) As Long

Dim lpShellExInfo As SHELLEXECUTEINFOA

With lpShellExInfo
.cbSize = Len(lpShellExInfo)
.lpVerb = "open"
.lpFile = vsCmdLine
.nShow = 0
.fMask = SEE_MASK_DOENVSUBST Or SEE_MASK_NOCLOSEPROCESS Or SEE_MASK_IDLIST
End With

If ShellExecuteEx(lpShellExInfo) Then
ShellEx = GetProcessId(lpShellExInfo.hProcess)
CloseHandle lpShellExInfo.hProcess
Else
ShellEx = 0
End If

End Function

++
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
9 sept. 2006 à 13:58
Nenni, aucune incompatibilité puisque qu'elle ne prend qu'un param 32 bits et en retourne un autre.
Il n'y a pas de syntaxe "officielle" des APIs en VB, la seule doc officielle est MSDN.
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
9 sept. 2006 à 13:54
(*) Sous VB6, il n'existe aucune syntaxe officielle de cette API GetProcessId - peut-être un problème de compatibilité | type de var
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
9 sept. 2006 à 13:38
Ok BruNews. Merci pour le lien vers cette API purement XP (et +).

Je viens de l'essayer avec cette syntaxe que je pense être bonne puisqu'elle renvoie bien une valeur (PID), mais ce PID n'apparait pas parmi les Thread.
Private Declare Function GetProcessId Lib "kernel32" (ByVal hProcessId As Long) As Long

Si qqun arrive à construire une structure de recherche convenable ...
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
9 sept. 2006 à 11:08
ShellERxecuteEx() retourne un hprocess valide (sinon à quoi bon...).
pid = GetProcessId(hprocess);
Tout ce qu'il faut ici:
http://www.vbfrance.com/forum.v2.aspx?ID=808860
Rejoignez-nous