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

Langage Delphi Discussion :

Redirection des entrées/sorties du process (ping)


Sujet :

Langage Delphi

  1. #1
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut Redirection des entrées/sorties du process (ping)
    Bonsoir,

    Je replonge dans mon bouquin pour essayer de comprendre le fonctionnement de diverses choses comme les redirections d'entrées/sorties, les threads, les sémaphores et les mutex.

    Pour le moment, j'en suis au premier stage. . J'ai un soucis avec le code ci dessous, c'est que rien ne se passe

    Voici le code :

    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
    procedure run(cmd:string; txt:TStrings);
    var
    PipeIn : THandle;
    PiPeOut : THandle;
    StartupInfo : TStartupInfo;
    ProcessInfo : TProcessInformation;
     
    Buffer:array[0..4096] of Char;
    NbRead:DWORD;
    begin
      CreatePipe(PipeIn,PiPeOut,nil,0);
      FillChar(StartupInfo, SizeOf(TStartupInfo),0);
      StartupInfo.cb := SizeOf(TStartupInfo);
      StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      StartupInfo.wShowWindow := SW_HIDE;
      StartupInfo.hStdInput := PipeIn;
      StartupInfo.hStdOutput:= PiPeOut;
      StartupInfo.hStdError := PiPeOut;
      CreateProcess(nil,PChar(cmd),nil,nil,true,0,nil,nil,StartupInfo,ProcessInfo);
      WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
      CloseHandle(PiPeOut);
      while ReadFile(PipeIn,Buffer,4096,NbRead,nil) do txt.Add(Buffer);
      CloseHandle(PipeIn);
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Screen.Cursor:=crHourGlass;
      run('ping.exe 127.0.0.1',memo1.Lines);
      Screen.Cursor:=crDefault;
      Beep;
    end;
    J'ai fait une recopie bête et méchante de l'exemple du livre mais je ne comprends pas trop ce qui cloche (surement qu'il ne passe pas le while), et pour être plus généraliste, je ne comprends pas trop la méthode du CreatePipe et du CreateProcess

    Si vous pouvez éclairer ma lanterne
    Merci

  2. #2
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Bonjour,

    J'ai refais un peu de tests ce matin et il s'avère que la commande ne se lance pas ...

    J'ai modifié la ligne

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    StartupInfo.wShowWindow := SW_HIDE;
    par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    StartupInfo.wShowWindow := SW_SHOWNORMAL;
    Et je vois que la console se lance bien, mais aucun texte à l'écran

    Je me suis dit, essayons autre chose, voir si ping.exe fonctionne bien via un CreateProcess tout simple, sans Pipe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TForm1.Button2Click(Sender: TObject);
    var
    StartupInfo : TStartupInfo;
    ProcessInfo : TProcessInformation;
    begin
      FillChar(StartupInfo, SizeOf(TStartupInfo),0);
      StartupInfo.cb := SizeOf(TStartupInfo);
      StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      StartupInfo.wShowWindow := SW_SHOWNORMAL;
      CreateProcess(nil,PChar('ping.exe 127.0.0.1'),nil,nil,true,0,nil,nil,StartupInfo,ProcessInfo);
    end;
    Avec ce code, toujours le même résultat, fenêtre vide

    Dernière chance, avec un ShellExecute classique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure TForm1.Button3Click(Sender: TObject);
    begin
      ShellExecute(0,'open',PChar('ping.exe'),'127.0.0.1',nil,SW_SHOWNORMAL);
    end;
    Hé bien là ça fonctionne. Je vois pas ce qui cloche. Et dire que ça fait 4 fois que je relis le chapitre des Redirections avec Threads et compagnies

    Merci

  3. #3
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    hello, ce code me dit quelque chose

    alors en effet il ne fonctionne pas bien sous 2K...voici une version modifiée

    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
     
    procedure run(cmd:string; txt:TStrings);
    var
    PipeIn : THandle;
    PiPeOut : THandle;
    Security : TSecurityAttributes;
    StartupInfo : TStartupInfo;
    ProcessInfo : TProcessInformation;
    Buffer:array[0..4096] of Char;
    NbRead:DWORD;
    begin
     With Security do begin
       nlength := SizeOf(TSecurityAttributes) ;
       binherithandle := true;
       lpsecuritydescriptor := nil;
      end;
      CreatePipe(PipeIn,PiPeOut,@Security,0);
      FillChar(StartupInfo, SizeOf(TStartupInfo),0);
      StartupInfo.cb := SizeOf(TStartupInfo);
      StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      StartupInfo.wShowWindow := SW_SHOW;
      StartupInfo.hStdInput := PipeIn;
      StartupInfo.hStdOutput:= PiPeOut;
      StartupInfo.hStdError := PiPeOut;
      CreateProcess(nil,PChar(cmd),nil,nil,true,0,nil,nil,StartupInfo,ProcessInfo);
      WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
      CloseHandle(PiPeOut);
      while ReadFile(PipeIn,Buffer,4096,NbRead,nil) do txt.Add(Buffer);
      CloseHandle(PipeIn);
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  4. #4
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Merci en!

    Par contre, il y a de petits soucis, notamment avec les accents. Peut-on modifier ce soucis?

    Seconde chose Vu que tu as modifié le code, peux tu me dire qu'est ce qui n'allait pas trop bien avec la version du bouquin? Et peux tu me dire, m'expliquer ce qu'est, ce que fait la procédure CreatePipe ainsi que CreateProcess (c'est un peu les paramètres que j'ai du mal à saisir ).

    Merci pour ton aide, je vais pouvoir m'attaquer au thread
    Bonne soirée

  5. #5
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Yep !

    Citation Envoyé par ero-sennin Voir le message
    peux tu me dire qu'est ce qui n'allait pas trop bien avec la version du bouquin?
    L'absence de la sécurité (D7 Studio, hein ? J'ai le même, )
    Mes 2 cts,
    --
    jp
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    le bouquin est sorti fin 2002...je pense qu'à l'époque je bossais encore sous 98, donc pas de notion de Security

    Sinon voici le détail des étapes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      CreatePipe(PipeIn,PiPeOut,@Security,0);
    ici on crée des "pipes" c'est à dire des flux d'entrée/sortie que l'on va pouvoir connecter sur une application...au même titre que les signes "<" et ">" en ligne de commande.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
      FillChar(StartupInfo, SizeOf(TStartupInfo),0);
      StartupInfo.cb := SizeOf(TStartupInfo);
      StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      StartupInfo.wShowWindow := SW_HIDE;
      StartupInfo.hStdInput := PipeIn;
      StartupInfo.hStdOutput:= PiPeOut;
      StartupInfo.hStdError := PiPeOut;
    ici on détermine les options de démarrage du process, dwFlags indique quels sont les champs renseignés dans la structure (les autres sont ignorés). On indique que l'application ne doit pas être affichée, et les entrées/sorties pointent sur les PIPES

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      CreateProcess(nil,PChar(cmd),nil,nil,true,0,nil,nil,StartupInfo,ProcessInfo);
    ça c'est la commande qui lance un nouveau process, contrairement à ShellExecute on va pouvoir récupérer les informations sur le process et notamment un handle qui va nous permettre de suivre son execution.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
    version simpliste, on bloque notre programme tant que le process lancé n'est pas terminé...à mettre dans un Thread ou dans une bouble avec une tempo à la place du INFINITE si on ne veux pas bloquer le programme.
    Vous avez peut-être déjà vu des programmes d'installation afficher une barre de progression sur une tache de fond...en fait le programme fait une estimation du temps d'execution du programme fils, et ajuste la barre en fonction du temps qui passe...si le programme prend plus de temps que prévu (genre un Firewall qui demande l'autorisation de lancer le programme), on se retrouve avec une barre de progression qui fait n'importe quoi

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      CloseHandle(PiPeOut);
    alors là franchement, c'est empirique, il se trouve que si on ne ferme pas PipeOut, la lecture de PipeIn ne fonctionne pas...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      while ReadFile(PipeIn,Buffer,4096,NbRead,nil) do txt.Add(Buffer);
      CloseHandle(PipeIn);
    avant de fermer PipeIn on va lire son contenu, qui correspond tout simplement à ce qui a été envoyé sur STD_OUTPUT et STD_ERROR.

    Note que dans cette version simpliste, on ne lit la réponse du programme qu'une fois celui-ci terminé...il doit être possible de le lire au fur et à mesure.

    Remarque: C'est exactement sur ce modèle que fonctionnent les programmes CGI sur un serveur Web.

    Pour le problème d'accent, c'est tout simplement parce que PING affiche son texte dans une police OEM, il faut utiliser OemToChar() pour convertir le texte en ANSI, ou utiliser une police OEM dans le Memo
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut


    Merci pour ces explications bien claires .

    Je comprends pourquoi ça bloquait et en ce qui concerne les accents, dès que j'ai Delphi sous la main, je vais tester la fonction OEMToChar .

    En ce qui concerne le CreateProcess, je suis allé voir ici pour avoir des infos supplémentaires.

    En tout cas, c'est top! Et dire que je pensais que le programme ne fonctionnait pas à cause de moi mais non, c'était un petit soucis de sécurité.

    Merci, je poursuis mon aventure avec les threads
    A+

  8. #8
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    Citation Envoyé par ero-sennin Voir le message
    En ce qui concerne le CreateProcess, je suis allé voir ici pour avoir des infos supplémentaires.
    ça marche aussi
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  9. #9
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Hé bien, je reviens vers vous pour un petit soucis de compréhension et d'utilisation de OemToChar.

    J'ai plusieurs questions
    - Peut-on éviter de passer par une variable de type char pour le Buffer ? Par exemple stocker le contenu de PipeIn dans une variable de type String.
    - Je n'arrive pas à convertir mon buffer pour qu'il soit compatible avec le type de caractères ANSI... peut on m'indiquer comment faire juste pour ma culture personnelle ?

    Je tiens à préciser qu'avant de poster quoi que ce soit, j'ai bien évidemment chercher comment faire, mais tout ce qui est Buffer c'est nouveau alors forcément, c'est pas très très clair dans mon esprit.

    Merci d'avance

  10. #10
    Expert éminent sénior
    Avatar de Cl@udius
    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2006
    Messages
    4 878
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 4 878
    Points : 10 008
    Points
    10 008
    Par défaut
    Salut
    Citation Envoyé par ero-sennin Voir le message
    - Peut-on éviter de passer par une variable de type char pour le Buffer ? Par exemple stocker le contenu de PipeIn dans une variable de type String.
    Je ne pense pas.

    Citation Envoyé par ero-sennin Voir le message
    - Je n'arrive pas à convertir mon buffer pour qu'il soit compatible avec le type de caractères ANSI... peut on m'indiquer comment faire juste pour ma culture personnelle ?
    J'essayerais de le convertir ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      OemToAnsi(Buffer, Buffer);
    @+ Claudius

  11. #11
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Citation Envoyé par Cl@audius
    J'essayerais de le convertir ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    OemToAnsi(Buffer, Buffer);
    Hum, ça ne fonctionne pas
    Voici le bout de code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    CreateProcess(nil,PChar(cmd),nil,nil,true,0,nil,nil,StartupInfo,ProcessInfo);
    WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
    CloseHandle(PiPeOut);
    OemToAnsi(Buffer,Buffer);
    while ReadFile(PipeIn,Buffer,4096,NbRead,nil) do txt.Add(Buffer);
    CloseHandle(PipeIn);
    A+

  12. #12
    Expert éminent sénior
    Avatar de Cl@udius
    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2006
    Messages
    4 878
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 4 878
    Points : 10 008
    Points
    10 008
    Par défaut
    Je le voyais plus comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    while ReadFile(PipeIn,Buffer,4096,NbRead,nil) do 
    begin
      OemToAnsi(Buffer, Buffer);
      txt.Add(Buffer);
    end;
    @+

  13. #13
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Citation Envoyé par Cl@udius Voir le message
    Je le voyais plus comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    while ReadFile(PipeIn,Buffer,4096,NbRead,nil) do 
    begin
      OemToAnsi(Buffer, Buffer);
      txt.Add(Buffer);
    end;
    @+
    Heu, un grand merci , ca fonctionne niquel
    A+

  14. #14
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Merci Messieurs pour cette discussion très interessante, j'ai ainsi pu obtenir, le résultat que je souhaitais

    Ah ce "CloseHandle(hWritePipe);" astucieux !

    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
    function TxxxExchanger.CallCmd(const CmdDirectory, CmdName, CmdParam: string; out ErrorCode, ExitCode: Integer; out ErrorMsg: string): Boolean;
    var
      StartupInfo: TStartupInfo;
      ProcessInfo: TProcessInformation;
      CmdLine: string; // utile pour le débogage
      SecurityAttr : TSecurityAttributes;
      hReadPipe, hWritePipe: Cardinal;
      PipeSize, PipeSizeHigh, PipeReaded: Cardinal;
      PipeBuf: array[0..4095]of Char;
      PipeText: string;
      I: Integer;
    begin
      ErrorMsg:= '';
      try
        SecurityAttr.nLength := SizeOf(TSecurityAttributes);
        SecurityAttr.lpSecurityDescriptor := nil;
        SecurityAttr.bInheritHandle := True;
        if CreatePipe(hReadPipe, hWritePipe, @SecurityAttr, 0) then
        begin
          try
            ZeroMemory(@StartupInfo, SizeOf(StartupInfo)); // GetStartupInfo(StartupInfo);
            StartupInfo.cb := SizeOf(StartupInfo);
            StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; // Active wShowWindow et hStdOutput/hStdError
            StartupInfo.wShowWindow := SW_HIDE;
            StartupInfo.hStdInput := hReadPipe;
            StartupInfo.hStdOutput := hWritePipe;
            StartupInfo.hStdError := hWritePipe;
            ZeroMemory(@ProcessInfo, SizeOf(ProcessInfo));
            CmdLine := Format('"%s%s" %s', [CmdDirectory, CmdName, CmdParam]);
            Result := CreateProcess(nil, PChar(CmdLine), @SecurityAttr, @SecurityAttr, True, 0, nil, PChar(CmdDirectory), StartupInfo, ProcessInfo);
            if Result then
            begin
              WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
              CloseHandle(hWritePipe); // Il faut fermer Write pour consulter Read
              hWritePipe := 0;
     
              PipeSize := GetFileSize(hReadPipe, @PipeSizeHigh);
              if PipeSize > 0 then
              begin
                SetLength(PipeText, PipeSize);
                ZeroMemory(@PipeText[1], PipeSize);
                ReadFile(hReadPipe, PipeText[1], PipeSize, PipeReaded, nil);
              end;
     
              I := Pos(',', PipeText);
              if I > 0 then
              begin
                if TryStrToInt(Copy(PipeText,1, I - 1), ErrorCode) then
                begin
                  ErrorMsg := Copy(PipeText, I + 1, MaxInt);
                end
                else
                begin
                  ErrorMsg := PipeText;
                  Result := False;
                end;
              end
              else
              begin
                ErrorMsg := PipeText;
                Result := False;
              end;
     
              if not GetExitCodeProcess(ProcessInfo.hProcess, Cardinal(ExitCode)) then
                ExitCode := -1;
     
              CloseHandle(ProcessInfo.hProcess); // The handles for both the process and the main thread must be closed through calls to CloseHandle
              CloseHandle(ProcessInfo.hThread);
            end;
          finally
            if hWritePipe > 0 then
              CloseHandle(hWritePipe);
            CloseHandle(hReadPipe);
          end;
        end
        else
          raise Exception.Create('Impossible de créer le Pipe');
      except
        on E: Exception do
        begin
          Result := False;
          OutputDebugString(PChar(Format('CallCmd Error %s, Message : %s', [E.ClassName, E.Message])));
        end;
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  15. #15
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Tient, en cherchant sur CreateProcess et CreatePipe, j'ai vu que l'on avait tous mal compris l'utilisation du Pipe, voici un code qui évite notre problème de CloseHandle ... et qui permet de récupérer en temps réel ...

    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
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    {* -----------------------------------------------------------------------------
    la fonction CallCmd permet de lancer un programme console, tout en récupérant en quasi temps-réel le contenu devant normalement s'y afficher
    @param CmdDirectory Dossier contenant le Fichier CmdName
    @param CmdName programme console à executer
    @param CmdParam paramètres de la ligne de commande
    @param ExitCode Code de Sortie renvoyé par le programme console, -1 si non récupéré
    @param OutputText chaine contenant tout ce qui aurait du s'afficher (canal sortie)
    @param ErrorText chaine contenant tout ce qui a été signalé comme erreurs (canal erreur)
    @param Delay indique le temps entre chaque cycle de lecture des canaux, détermine la fréquence de lancement de WaitEvent, par défaut, cela attend que le programme console se termine
    @param WaitEvent procédure à lancer lorsque le Delay est écoulé, Output et Error contiennent les derniers éléments envoyés par le programme console sur les canaux depuis le dernier délai, AbortProcess indique si la processus doit être arrêté
    @param PipeMaxSize défini la taille maximal que l'on lit à chaque chaque cycle de lecture des canaux, si zéro, taille non limitée par défaut
    @return Indique si le programme a été lancé 
    ------------------------------------------------------------------------------ }
    function CallCmd(const CmdDirectory, CmdName, CmdParam: string; out ExitCode: Int64; out OutputText: string; out ErrorText: string; Delay: Cardinal = INFINITE; WaitEvent: TCallCmdEvent = nil; PipeMaxSize: Cardinal = 0): Boolean;
    var
      StartupInfo: TStartupInfo;
      ProcessInfo: TProcessInformation;
      CmdLine: string; // utile pour le débogage
      SecurityAttr : TSecurityAttributes;
      hReadPipeInput, hWritePipeInput: Cardinal;
      hReadPipeOutput, hWritePipeOutput: Cardinal;
      hReadPipeError, hWritePipeError: Cardinal;
      Terminated: Boolean;
      AbortProcess: Boolean;
      HandleFunctionProcess: Cardinal;
     
      function ReadPipe(Handle: Cardinal; out Buf: string): Boolean;
      const
        MAX_INT: Cardinal = MaxInt;
      var
        PipeSize: Cardinal;
        PipeToRead, PipeReaded: Cardinal;
      begin
        PipeSize := GetFileSize(Handle, nil); // On oublie si cela dépasse 2Go ... normalement c'est 4Ko
        if (PipeMaxSize > 0) and (PipeSize > PipeMaxSize) then
          PipeToRead := PipeMaxSize
        else
          PipeToRead := PipeSize;
     
        Result := PipeToRead > 0;
        if Result then
        begin
          SetLength(Buf, PipeToRead);
          ZeroMemory(@Buf[1], PipeToRead);
          ReadFile(Handle, Buf[1], PipeToRead, PipeReaded, nil);
        end;
      end;
     
      procedure ReadPipes();
      var
        DeltaOutputText: string;
        DeltaErrorText: string;
      begin
        if ReadPipe(hReadPipeOutput, DeltaOutputText) then
          OutputText := OutputText + DeltaOutputText;
        if ReadPipe(hReadPipeError, DeltaErrorText) then
          ErrorText := ErrorText + DeltaErrorText;
        if Assigned(WaitEvent) then
          WaitEvent(DeltaOutputText, DeltaErrorText, AbortProcess);
      end;
     
    begin
      (*
      Result := True;
      OutputText := 'Dummy Output';
      ErrorText := 'Dummy Error';
      ErrorCode := 0;
      Exit;
      *)
      OutputText := '';
      ErrorText := '';
      try
        SecurityAttr.nLength := SizeOf(TSecurityAttributes);
        SecurityAttr.lpSecurityDescriptor := nil;
        SecurityAttr.bInheritHandle := True;
        if CreatePipe(hReadPipeInput, hWritePipeInput, @SecurityAttr, 0) and
          CreatePipe(hReadPipeOutput, hWritePipeOutput, @SecurityAttr, 0) and
          CreatePipe(hReadPipeError, hWritePipeError, @SecurityAttr, 0) then
        begin
          try
            ZeroMemory(@StartupInfo, SizeOf(StartupInfo)); // GetStartupInfo(StartupInfo);
            StartupInfo.cb := SizeOf(StartupInfo);
            StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; // Active wShowWindow et hStdOutput/hStdError
            StartupInfo.wShowWindow := SW_HIDE;
            StartupInfo.hStdInput := hReadPipeInput;
            StartupInfo.hStdOutput := hWritePipeOutput;
            StartupInfo.hStdError := hWritePipeError;
            ZeroMemory(@ProcessInfo, SizeOf(ProcessInfo));
            CmdLine := Format('"%s%s" %s', [IncludeTrailingPathDelimiter(CmdDirectory), CmdName, CmdParam]);
            Result := CreateProcess(nil, PChar(CmdLine), @SecurityAttr, @SecurityAttr, True, 0, nil, PChar(CmdDirectory), StartupInfo, ProcessInfo);
            if Result then
            begin
              try
                Terminated := False;
                AbortProcess := False;
                while not Terminated do
                begin
                  case WaitForSingleObject(ProcessInfo.hProcess, Delay) of
                    WAIT_OBJECT_0 :
                      begin
                        ReadPipes();
                        Terminated := True;
                      end;
                    WAIT_ABANDONED : Terminated := True;
                    WAIT_TIMEOUT :
                      begin
                        ReadPipes();
                        Terminated := Delay = INFINITE;
                      end;
                    WAIT_FAILED: Abort;
                  else
                    Terminated := True;
                  end;
     
                  if AbortProcess then
                  begin
                   HandleFunctionProcess := OpenProcess(PROCESS_TERMINATE, False, ProcessInfo.dwProcessId);
                   if HandleFunctionProcess > 0 then
                   begin
                     TerminateProcess(HandleFunctionProcess, 0);
                     CloseHandle(HandleFunctionProcess);
                   end;
                  end;
                end;
     
                TULargeInteger(ExitCode).HighPart := 0;
                if not GetExitCodeProcess(ProcessInfo.hProcess, TULargeInteger(ExitCode).LowPart) then
                  ExitCode := -1;
              finally
                CloseHandle(ProcessInfo.hThread);
                CloseHandle(ProcessInfo.hProcess); // The handles for both the process and the main thread must be closed through calls to CloseHandle
              end;
            end;
          finally
            CloseHandle(hReadPipeInput);
            CloseHandle(hWritePipeInput);
            CloseHandle(hReadPipeOutput);
            CloseHandle(hWritePipeOutput);
            CloseHandle(hReadPipeError);
            CloseHandle(hWritePipeError);
          end;
        end
        else
          raise Exception.Create('Impossible de créer les Pipes');
      except
        on E: Exception do
        begin
          OutputDebugString(PChar(Format('epcWindows.CallCmd Error %s, Message : %s', [E.ClassName, E.Message])));
          raise;
        end;
      end;
    end;
    EDIT : voici une nouvelle version, qui en plus, permet d'arrêter le processus, il suffit de mettre un application.processMessages dans l'Event ...

    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
    var
      _AbortCallCmd: Boolean = False;
     
    procedure CallCmdEvent(const Output, Error: string; var AbortProcess: Boolean);
    begin
      MainForm.MemoOutput.Lines.Add(Output);
      MainForm.MemoError.Lines.Add(Error);
     
      Application.ProcessMessages();
      _AbortProcess := AbortCallCmd;
    end;
     
    procedure TMainForm.BtnTestDOSClick(Sender: TObject);
    var
        ExitCode: Int64;
        OutPutText: string;
        ErrorText: string;
    begin
      _AbortCallCmd := False;
     
      epcWindows.CallCmd(
        ExtractFileDir(Application.ExeName),
        'TestLong.Bat',
        '',
        ExitCode,
        OutPutText,
        ErrorText,
        10,
        @CallCmdEvent
       )
    end;
     
    procedure TMainForm.BtnStopDOSClick(Sender: TObject);
    begin
      _AbortCallCmd := True;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  16. #16
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 27
    Points : 27
    Points
    27
    Par défaut
    Bonjour,
    j'ai lu vos réponses, il se trouve que je suis dans le même problème que vous !
    Je doit récupérer le sortie d'erreur d'un programme que j'utilise pour synchroniser des dossiers (Rsync).
    J'ai créé un pipe mais je n'ai jamais rien dedans à lire ...

    J'ai commencé par la même "simple", puis essayer la dernière citée, qui me génère des erreurs de segmentation à la création des pipes.

    Pourriez vous m'aider ?

    Merci !

  17. #17
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Personnellement, je ne l'ai testé qu'avec un fichier .bat et un programme console d'un partenaire, qui n'écrit que peu de chose dans la console ... je vais bientôt le tester avec php.exe pour un traitement quasi-temps de fichier, et j'espère avoir un affichage genre barre de progression, pour cela je compte sur une écriture de php sur le stdout ... je reviens, si j'ai des soucis comme le tiens, je n'ai rien vu de tel ... essaye d'affecter des pipes qu'à stdout ... il est fort possible, que lorsqu'il y a bcp d'écriture sur la console qu'il se bloque, donc avec un Semaphore entre le process ou le sous programme, il faudrait protéger l'écriture et la lecture ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  18. #18
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 27
    Points : 27
    Points
    27
    Par défaut
    Merci ShaiLeTroll

    voici ce que j'ai :
    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
     
     
    string commande =(string)RSYNC+" "+config->options+" --password-file="+FICHIER_ADR_MAC+" "+chemin+" "+synchro->nom_machine+"@"+synchro->ip+"::"+synchro->nom_groupe+"/"+synchro->nom_machine+"-"+synchro->nom_utilisateur+"/ ; " ;
     
     
    LPTSTR szCmdline =(LPTSTR)commande.c_str();                       STARTUPINFO startupinfo;
    PROCESS_INFORMATION processinfo;
     
    HANDLE PipeInputRead,PipeInputWrite;
    HANDLE PipeOutputRead,PipeOutputWrite;
     
    SECURITY_ATTRIBUTES Security;
    Security.nLength = sizeof(SECURITY_ATTRIBUTES); 
    Security.bInheritHandle = TRUE; 
    Security.lpSecurityDescriptor = NULL;
     
    DWORD ret;
    DWORD NbRead;
    string message="";
    char Buffer[65536];
    memset(&startupinfo,0,sizeof(startupinfo));
    memset(&processinfo,0,sizeof(processinfo));
     
     
     
    if ( ! CreatePipe(&PipeInputRead,&PipeInputWrite,&Security,0)&& CreatePipe(&PipeOutputRead,&PipeOutputWrite,&Security,0))
                             exit(1);
     
     
    startupinfo.cb=sizeof(startupinfo);
    startupinfo.dwFlags=STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
     
    startupinfo.wShowWindow=SW_HIDE;
    startupinfo.hStdInput = PipeInputRead;
    startupinfo.hStdOutput= PipeOutputWrite;
    startupinfo.hStdError = PipeOutputWrite;
     
    if (CreateProcess(NULL,szCmdline,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&startupinfo,&processinfo))
                                {
                                  CloseHandle(processinfo.hThread);
                                  while ( (WaitForSingleObject(processinfo.hProcess,1))==WAIT_TIMEOUT); 
                                  {
                                        ReadFile(PipeOutputRead,Buffer,65536,&NbRead,NULL);
                                        message+=Buffer;
                                  }
                                  WaitForSingleObject(processinfo.hProcess,INFINITE);
                                  ReadFile(PipeOutputRead,Buffer,65536,&NbRead,NULL);
                                  message+=Buffer;
                                  GetExitCodeProcess(processinfo.hProcess, &ret);
     
                                  if( ret != STILL_ACTIVE)
                                  {
                                      CloseHandle(processinfo.hProcess);
                                  } 
                                  else
                                  {
                                      CloseHandle(PipeOutputWrite);
                                      CloseHandle(PipeInputRead);
                                      
                                  }
                                  CloseHandle(PipeOutputWrite);
                                  CloseHandle(PipeInputRead);
                                  CloseHandle(PipeInputWrite);
     
     
                                  CloseHandle(PipeOutputRead);
     
                                } 
                           else
                           {
                               throw Erreur_Rsync(1,"erreur lancement Rsync");
                           }
                           if( ret != 0)
                           {
                                string message_erreur="Problème lors de la synchronisation: "+get_Message_Erreur(ret)+message;
                                throw Erreur_Rsync(ret,message_erreur);   
                           }
    je ne récupère jamais rien dans stdOut...
    J'ai essayer avec une commande "ping 10.0.0.12" et idem, rien de récupérer.

    Quelqu'un a un idée.

  19. #19
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Salut.

    C'est-y pas du C, ton code ? Auquel cas, je crois que tu t'es trompé de forum, non ?
    --
    jp
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  20. #20
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 27
    Points : 27
    Points
    27
    Par défaut
    Salut Jp !

    C'est même du C++ !

    Mais la demarche étant la même, je pensait que peut être quelqu'un aurait une idée.
    J'ai lu ce qu'on fait les autres dans se post, se n'est pas le même langage mais les algos sont les mêmes !

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. rediriger des entrées/sorties
    Par the_only_kraft dans le forum Général Python
    Réponses: 2
    Dernier message: 12/09/2008, 13h34
  2. Adresse Mémoire des Entrées-Sorties
    Par B-NeT dans le forum Windows
    Réponses: 1
    Dernier message: 10/07/2008, 16h04
  3. Redirection des entrées ET sorties : << ET | tee
    Par Gaillac dans le forum Shell et commandes GNU
    Réponses: 5
    Dernier message: 24/04/2008, 16h44
  4. gestion des entrées-sorties
    Par bandit_debutant dans le forum Entrée/Sortie
    Réponses: 8
    Dernier message: 25/11/2006, 14h55

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