IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

ShellExecute / CreateProcess / ShellExecuteEx


Sujet :

API, COM et SDKs Delphi

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 114
    Points : 133
    Points
    133
    Par défaut ShellExecute / CreateProcess / ShellExecuteEx
    Bonjour à tous,

    Je cherche à faire qqchose d'archi classique : ouvrir un fichier et attendre que l'utilisateur ait fini pour revenir dans l'application. J'ai cherché à utiliser les moyens suivants :

    ShellExecute: Rien à redire. On lui passe le nom d'un fichier quelque soit son type (Pdf, doc, jpg...), il trouve le programme associé et ouvre le fichier avec. Seul problème: dès que le programme en question est lancé, on poursuit l'exécution du code. Donc ca va pas dans mon cas.


    CreateProcess + WaitForSingleObject: la on peut effectivement créer un processus et attendre qu'il soit terminé pour poursuivre. Seul problème: on ne peut lancer que des exécutables, et non pas des fichiers nus (que Windows se chargerait d'ouvrir avec le bon programme).
    Voir exemple sur le site (que dis-je, la bible!) de Michel Bardou :

    http://www.phidels.com/php/index.php...zip.php3&id=66

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function LanceAppliAttenteFin(NomFichier:string):boolean;
    { LanceAppliAttenteFin renvoie true si le lancement s'est bien passé   }
    var
      StartInfo : TStartupInfo;
      ProcessInformation : TProcessInformation;
    begin
      result:=true;
      ZeroMemory(@StartInfo, sizeof(StartInfo)); // remplie de 0 StartInfo
      StartInfo.cb:=sizeof(StartInfo);
      if CreateProcess(nil,PChar(NomFichier),nil,nil,true,0,nil,nil,StartInfo,ProcessInformation)
      then WaitForSingleObject(ProcessInformation.hProcess, INFINITE)// attend que l'application désignée par le handle ProcessInformation.hProcess soit terminée
      else result:=false;
    end;

    ShellExecuteEx: Différence avec ShellExecute: on peut récupérer l'id du process. Du coup, après avoir demandé à Windows d'ouvrir le fichier souhaité, on peut attendre que le process soit terminé. Le parfait mariage entre la 1ere et la 2eme méthode. Mais léger problème: le "scan" d'attente de fin de process prend 100% des ressources du CPU.

    Méthode utilisée : http://delphi.about.com/od/windowssh...uteprogram.htm

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    uses ShellApi;
    ...
    var
       SEInfo: TShellExecuteInfo;
       ExitCode: DWORD;
       ExecuteFile, ParamString, StartInString: string;
    begin
       ExecuteFile:='c:\temp\monfichier.pdf';
     
       FillChar(SEInfo, SizeOf(SEInfo), 0) ;
       SEInfo.cbSize := SizeOf(TShellExecuteInfo) ;
       with SEInfo do begin
         fMask := SEE_MASK_NOCLOSEPROCESS;
         Wnd := Application.Handle;
         lpFile := PChar(ExecuteFile) ;
    {
    ParamString can contain the
    application parameters.
    }
    // lpParameters := PChar(ParamString) ;
    {
    StartInString specifies the
    name of the working directory.
    If ommited, the current directory is used.
    }
    // lpDirectory := PChar(StartInString) ;
         nShow := SW_SHOWNORMAL;
       end;
       if ShellExecuteEx(@SEInfo) then begin
         repeat
           Application.ProcessMessages;
           GetExitCodeProcess(SEInfo.hProcess, ExitCode) ;
         until (ExitCode <> STILL_ACTIVE) or
    Application.Terminated;
         ShowMessage('Fichier PDF fermé') ;
       end
       else ShowMessage('Impossible de visionner le fichier PDF!') ;
    end;
    Cette dernière méthode est assez séduisante, mais alors le fait que le processeur soit pris a 100% pendant la boucle repeat ..until est loin d'être satisfaisant.

    Est-ce que quelqu'un voit un moyen de rendre cette boucle moins gourmande ? J'avais pensé mettre un sleep(1000) dans la boucle, mais je ne trouve pas ca très élégant ?

  2. #2
    Membre chevronné
    Avatar de Clorish
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    2 474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 2 474
    Points : 2 158
    Points
    2 158
    Par défaut
    et si tu executais le test d'atente dans un Thread secondaire de ton application ?
    Un message, un evenement, ou tout autre moyen de communication inter thread te permettra de mettre a jour une variable booleene lorsque l'attente est fini.
    Cette variable booleen mise a false lors du lancement de l'applciation servira a bloquer le thread principal qui se basera dessus pour lancer ou non les operations a bloquer.

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 114
    Points : 133
    Points
    133
    Par défaut
    Merci pour ta réponse Clorish.

    J'avais bien pensé à un thread, mais je n'ai qu'une vague notion de ces "bestioles" que je n'ai encore jamais utilisé. Ca me semble un peu compliqué pour mes connaissances très limitées en Delphi.

    Bon, j'ai testé avec un sleep et ca fonctionne..on réduit l'utilisation du CPU de 100% a quelques pouillèmes de pourcent.

    Cela dit, si quelqu'un se sent la force de programmer le code correspondant a l'utilisation des threads énoncés ci-dessus, je pense que ca pourra toujours resservir (ca aurait également pu faire une bonne initiation à ces petites choses). Sinon je vais me contenter du sleep.

    Je marque "Résolu" quand meme.

  4. #4
    Membre chevronné
    Avatar de Clorish
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    2 474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 2 474
    Points : 2 158
    Points
    2 158
    Par défaut
    evite de faire un sleep dans le thread principal .. tu endort ton programme completement et ne reagira plus du tout.

    Pour le thread c'est vraiement pas compliqué :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Type 
      TMonThread = Class(TThread)
      Public
        PRocedure Execute; override;
      end;
     
    Procedure TMonThread.Execute;
    Begin
    <appel a ShellExecute>
    <Boucle de test sans Application.ProcessMessage mais avec Sleep>
    End;
    Pour l'appel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    MonThread := TMonThread.Create;
    ou :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MonThread := TMonThread.Create(true);
    TMonThread.Resume
    A pres tout se comporte comme une classe tout simple.
    Tu peux y creer des property, des methodes, a utiliser avant le resume et en mode de creation suspendu.

    Apres je t'engage a consulter l'aide ou la faq a ce sujet.

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Février 2009
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 18
    Points : 23
    Points
    23
    Par défaut Ma solution
    Voici la solution que j'utilise :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
     
    {*------------------------------------------------------------------------------
      Effectuer un ShellExecute et attendre la fin du process lancé
     
      @Author  nicolassalocin
      @Version 24/02/2009
      @Param   vHandle     Handle de la fenêtre parent pour toute fenêtre de message
                           que le système pourrait produire pendant l'exécution de
                           cette fonction
     
      @Param   vOperation  Opération à réaliser :
                           - "open"	: La fonction ouvre le fichier spécifié.
                           Le fichier à ouvrir peut être un exécutable, un document
                           ou un répertoire.
                           - "print" : La fonction imprime le fichier spécifié.
                           Le fichier à imprimer doit être un document.
                           Si le fichier à imprimer est un exécutalbe, la fonction
                           ouvre le fichier comme si "open" avait été spécifié.
                           - "explore" : La fonction explore le répertoire spécifié
     
      @Param   vFichier    Fichier à ouvrir ou à imprimer ou répertoire à ouvrir ou
                           explorer. La fonction peut ouvrir un exécutable ou un
                           document ou imprimer un document.
     
      @Param   vParametres Paramètres à  passer à l'application si le fichier
                           spécifié est un exécutable. Si le fichier spécifié est
                           un document, ne rien passer.
     
      @Param   vRepertoire Répertoire d'exécution. S'il n'est pas indiqué le
                           répertoire en cours est utilisé
     
      @Param   vAffichage  Indique comment la fenêtre doit être montrée. Utiliser
                           les variables Delphi :
                           - SW_HIDE,
                           - SW_SHOWNORMAL,
                           - SW_NORMAL,
                           - SW_SHOWMINIMIZED,
                           - SW_SHOWMAXIMIZED,
                           - etc...
     
      @Param   vDuree      Durée d'attente de la fin d'exécution en millisecondes.
                           Si la fin de l'exécution n'est pas terminée dans le temps
                           imparti, la fonction se termine.
                           Pour attendre à l'infini utiliser la variable INFINITE.
     
      @Return              Renvoi un code d'erreur :
                           - NO_ERROR                L'attente a aboutit
                           - WAIT_FAILED             L'attente n'a pas aboutit
                           - ERROR_FILE_NOT_FOUND	   Le fichier spécifié n'a pas été
                                                     trouvé
                           - ERROR_PATH_NOT_FOUND	   Le chemin spécifié n'a pas été
                                                     trouvé
                           - ERROR_DDE_FAIL	         La transaction DDE a echoué
                           - ERROR_NO_ASSOCIATION	   Il n'y a pas d'application
                                                     associée à l'extension du
                                                     fichier
                           - ERROR_ACCESS_DENIED     Accès refusé
                           - ERROR_DLL_NOT_FOUND     DLL introuvable
                           - ERROR_CANCELLED	       La fonction a demandé à
                                                     l'utilisateur d'indiquer
                                                     l'emplacement de l'application
                                                     mais l'utilisateur a annulé la
                                                     demande
                           - ERROR_NOT_ENOUGH_MEMORY Mémoire insuffisante
                           - ERROR_SHARING_VIOLATION Une violation de partage a été
                                                     rencontrée
     
      @Comment Exemples d'utilisation :
               //Pour une application
               if ShellExecuteAndWait(
                                      Application.Handle,
                                      'open',
                                      'C:\_DELPHI\CODE_TO_DOC\DCTD_cmd.exe',
                                      'VERIF_SRV.xml',
                                      'X:\SIS_BETA\VERIF_SRV\CODE_TO_DOC\',
                                      SW_SHOWNORMAL,
                                      INFINITE
                                     ) = NO_ERROR then...
     
               //Pour un document
               if ShellExecuteAndWait(
                                      Application.Handle,
                                      'print',
                                      'C:\Mes documents\toto.pdf',
                                      '',
                                      '',
                                      SW_SHOWNORMAL,
                                      INFINITE
                                     ) = NO_ERROR then...
    -------------------------------------------------------------------------------}
    function ShellExecuteAndWait(
                                 vHandle     : HWND;
                                 vOperation  : string;
                                 vFichier    : string;
                                 vParametres : string;
                                 vRepertoire : string;
                                 vAffichage  : Integer;
                                 vDuree      : DWORD
                                ) : Cardinal;
    var
      vSEInfo   : TShellExecuteInfo;
    begin
      //Initialisation de la structure ShellExecuteInfo
      ZeroMemory(@vSEInfo, SizeOf(vSEInfo));
     
      //Remplissage de la structure ShellExecuteInfo
      vSEInfo.cbSize       := SizeOf(vSEInfo);
      vSEInfo.fMask        := SEE_MASK_NOCLOSEPROCESS;
      vSEInfo.Wnd          := vHandle;
      vSEInfo.lpVerb       := PAnsiChar(vOperation);
      vSEInfo.lpFile       := PAnsiChar(vFichier);
      vSEInfo.lpParameters := PAnsiChar(vParametres);
      vSEInfo.lpDirectory  := PAnsiChar(vRepertoire);
      vSEInfo.nShow        := vAffichage;
     
      //Execution
      if ShellExecuteEx(@vSEInfo) then
      begin
        //ShellExecuteEx OK : attendre la fin du process
        Result := WaitForSingleObject(vSEInfo.hProcess, vDuree);
      end
      else
      begin
        //ShellExecuteEx KO : renvoyer l'erreur
        Result := GetLastError();
      end;
    end;

  6. #6
    Membre averti Avatar de dacid
    Homme Profil pro
    Inscrit en
    Juin 2003
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 1 064
    Points : 420
    Points
    420
    Par défaut
    Bonjour @ tous,

    Ca à l'air extra cette solution nicolas,
    Seulement, quand je l'utilise, il fige l'application et ne démarre l'exe indiqué que lorsque j'ai tuée mon appli principale à travers le gestionnaire des taches.

    Pourtant, j'utilise exactement l'exemple fourni pour un exe.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      if FileExists(nomFicAppliPS) then begin
        if ShellExecuteAndWait(Application.Handle,'open',nomFicAppliPS,'','',SW_SHOWNORMAL,INFINITE)=NO_ERROR then begin //Pour une application
        ...
    Je lance le traitement depuis un clic de bouton.
    L'exe lancé est un setup de "Adobe postscript driver"... Donc une installation/dépaquetage complet.

    Y a t-il un point que j'aurais oublié ?

    Merci d'avance.

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 114
    Points : 133
    Points
    133
    Par défaut
    Finalement j'utilise ca comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    function ShellExecuteAttenteFin(ExecuteFile:string;WorkingDir:string):boolean;
    var
       SEInfo: TShellExecuteInfo;
       ExitCode: DWORD;
    begin
       FillChar(SEInfo, SizeOf(SEInfo), 0) ;
       SEInfo.cbSize := SizeOf(TShellExecuteInfo) ;
       with SEInfo do begin
         fMask := SEE_MASK_NOCLOSEPROCESS;
         Wnd := Application.Handle;
         lpFile := PChar(ExecuteFile) ;
    {
    ParamString can contain the  application parameters.
    }
    // lpParameters := PChar(ParamString) ;
     
         lpDirectory := PChar(WorkingDir) ;
         nShow :=  SW_SHOWNORMAL;  
       end;
       if ShellExecuteEx(@SEInfo) then begin
         repeat
           Delay(500); // Evite boucle 100% CPU
           GetExitCodeProcess(SEInfo.hProcess, ExitCode) ;
         until (ExitCode <> STILL_ACTIVE) or
         Application.Terminated;
     
       end
       else ShowMessage('Error starting ' + ExecuteFile) ;
     
       Result := true;
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
       If FileExists(Target_dir + Target_file) then
        begin
              ShellExecuteAttenteFin(Target_dir + Target_file,Target_dir);
        end;
    ne pas oublier de faire un

    uses ShellApi;

    en début de programme.

  8. #8
    Membre averti Avatar de dacid
    Homme Profil pro
    Inscrit en
    Juin 2003
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 1 064
    Points : 420
    Points
    420
    Par défaut
    Bonjour Wizdom,

    Merci pour ta rapidité.
    Delphi ne connait pas Delai... J'ai mis un Sleep.

    Sinon, ça marche impec, sauf que dans mon cas, c'est spécial.

    En fait, je pense que je lance un exe qui en lance d'autres, si bien que le traitement de mon programme se poursuit alors qu'on est en plein dans la procédure d'installation de mon "sous exe".

    Mais je ne pense pas que ce soit possible d'intercepter ce cas de figure, je vais devoir procéder différemment.

    Merci quand même. ;-)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Débutant] Comment attendre la fin d'un process lancé par ShellExecute ou ShellExecuteEx
    Par Godzestla dans le forum C++Builder
    Réponses: 3
    Dernier message: 19/05/2011, 12h34
  2. CreateProcess et ShellExecute
    Par Samyhijodelaluna dans le forum MFC
    Réponses: 23
    Dernier message: 11/05/2006, 16h17
  3. CreateProcess, ShellExecute
    Par cat1982 dans le forum Langage
    Réponses: 5
    Dernier message: 03/05/2006, 15h32
  4. [EXECUTION] CreateProcess / ShellExecute
    Par jmdeffet dans le forum API, COM et SDKs
    Réponses: 2
    Dernier message: 14/10/2004, 10h33
  5. Réponses: 2
    Dernier message: 18/11/2002, 09h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo