begin process at 2008 09 06 20:26:43
1 237 953 membres
335 nouveaux aujourd'hui
14 314 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

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


Information sur la source

Catégorie :Fichier / Disque Classé sous : shell, shellexecute, handle, retrouver, lancer Niveau : Initié Date de création : 09/09/2006 Date de mise à jour : 10/09/2006 20:03:51 Vu / téléchargé: 10 110 / 985

Note :
Aucune note

Commentaire sur cette source (30)
Ajouter un commentaire et/ou une note


Description

Bon, ça y est. Après un week-end entier à m'arracher le peu de cheveux qui me reste, voilà une version complète.
Deux projets dans ce ZIP :
- La 1ere version utilise la fonction Shell (tout en gardant la possibilité de lancer directement un fichier TXT)
- La 2ème version utilise ShellExecuteEx qui recherche elle même le programme associé. Mais la difficulté majeure était de retrouver le lien entre le paramètre hProcessId qu'elle fournit, avec le handle réelle de la fenêtre affichée.

Je remercie BruNews, RenField et Draluorg au travers de leurs échanges.
J'étais dans la bonne voie, mais c'est l'organisation interne de Windows que je ne connaissais pas suffisemment.

Ceci répondra à la question de LOLPOP sur le forum (au moins).
Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

09 septembre 2006 03:50:00 :
Avec un ti zip, c'est mieux (quel tête en l'air, ce Jack)
09 septembre 2006 03:52:47 :
Et en plus, je me tromp de zip (là, c'est quel con, qu'il faut dire)
10 septembre 2006 20:03:51 :
Voir commentaires
  • signaler à un administrateur
    Commentaire de BruNews le 09/09/2006 11:08:16 administrateur CS

    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

  • signaler à un administrateur
    Commentaire de jack le 09/09/2006 13:38:25 administrateur CS

    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 ...

  • signaler à un administrateur
    Commentaire de jack le 09/09/2006 13:54:51 administrateur CS

    (*) Sous VB6, il n'existe aucune syntaxe officielle de cette API GetProcessId - peut-être un problème de compatibilité | type de var

  • signaler à un administrateur
    Commentaire de BruNews le 09/09/2006 13:58:06 administrateur CS

    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.

  • signaler à un administrateur
    Commentaire de draluorg le 09/09/2006 15:56:10

    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

    ++

  • signaler à un administrateur
    Commentaire de Renfield le 09/09/2006 17:04:47 administrateur CS

    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

  • signaler à un administrateur
    Commentaire de jack le 09/09/2006 19:13:14 administrateur CS

    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.

  • signaler à un administrateur
    Commentaire de draluorg le 09/09/2006 19:43:29

    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

    ++

  • signaler à un administrateur
    Commentaire de jack le 09/09/2006 20:38:55 administrateur CS

    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.

  • signaler à un administrateur
    Commentaire de jack le 09/09/2006 20:44:24 administrateur CS

    Pas d'accord avec toi Draluorg : hProcess n'est pas un Handle de fenêtre, mais un handle de Process

  • signaler à un administrateur
    Commentaire de draluorg le 09/09/2006 20:49:34

    <Pas d'accord avec toi Draluorg : hProcess n'est pas un Handle>

    Eh et c'est quoi alors ?
    Si ca renvoi le Pid en faisant un GetProcessId dessus c'est que c'est son handle... non ?

    ++

  • signaler à un administrateur
    Commentaire de BruNews le 09/09/2006 22:04:25 administrateur CS

    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.

  • signaler à un administrateur
    Commentaire de jack le 10/09/2006 04:36:28 administrateur CS

    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 !

  • signaler à un administrateur
    Commentaire de Renfield le 10/09/2006 10:52:32 administrateur CS

    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

  • signaler à un administrateur
    Commentaire de Renfield le 10/09/2006 11:34:10 administrateur CS

    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

  • signaler à un administrateur
    Commentaire de jack le 10/09/2006 13:34:38 administrateur CS

    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.

  • signaler à un administrateur
    Commentaire de draluorg le 10/09/2006 13:42:25

    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!

    ++

  • signaler à un administrateur
    Commentaire de jack le 10/09/2006 13:43:11 administrateur CS

    PS : Ne fonctionne pas en lançant un fichier PDF par exemple, Calc.exe étant trop particulier

  • signaler à un administrateur
    Commentaire de BruNews le 10/09/2006 13:54:17 administrateur CS

    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.

  • signaler à un administrateur
    Commentaire de jack le 10/09/2006 17:10:25 administrateur CS

    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)

  • signaler à un administrateur
    Commentaire de jack le 10/09/2006 20:16:05 administrateur CS

    Voilà la version finale avec ShellExecuteEx (MàJ)
    Une partie de plaisir !

  • signaler à un administrateur
    Commentaire de Ulala2 le 12/09/2006 21:54:21

    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.

  • signaler à un administrateur
    Commentaire de BruNews le 12/09/2006 22:11:55 administrateur CS

    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).

  • signaler à un administrateur
    Commentaire de draluorg le 12/09/2006 23:53:14

    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...)

    ++

  • signaler à un administrateur
    Commentaire de Ulala2 le 13/09/2006 00:24:53

    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.

  • signaler à un administrateur
    Commentaire de Renfield le 13/09/2006 07:52:00 administrateur CS

    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^^)

  • signaler à un administrateur
    Commentaire de Ulala2 le 13/09/2006 08:02:45

    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 :)

  • signaler à un administrateur
    Commentaire de chricha le 16/09/2006 22:52:35

    "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.

  • signaler à un administrateur
    Commentaire de xeroc le 10/08/2007 11:04:36

    Bonjour,

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

    Merci à toi

  • signaler à un administrateur
    Commentaire de epasquier le 02/09/2008 22:26:56

    Bonjour,
    Il faut corriger la ligne "SEI.hProcess > 0" par "SEI.hProcess <> 0" !

    Eric.

Ajouter un commentaire

Pub



Appels d'offres

CalendriCode

Septembre 2008
LMMJVSD
1234567
891011121314
15161718192021
22232425262728
2930     

Téléchargements

Logiciels à télécharger sur le même thème :

Boutique

Boutique de goodies CodeS-SourceS