Executing External Programs/fr

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) français (fr) italiano (it) 日本語 (ja) Nederlands (nl) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Introduction

Il y a différentes manière de lancer un programme externe, mais nous ne nous concentrerons que sur une seule : TProcess.

Si vous utilisez toujours ShellExecute et/ou WinExec sous Delphi, vous devriez utilisez TProcess comme alternative sous FPC/Lazarus (cela fonctionne également si vous l'utilisez sous Linux, car TProcess est multi-plateforme).

Note : FPC/Lazarus supporte ShellExecute et/ou WinExec, mais uniquement sous Win32. Si votre programme est multi-plateforme, utilisez TProcess, c'est la meilleure solution !

SysUtils.ExecuteProcess

La manière la plus simple si vous n'avez pas besoin de pipes ou toute forme de contrôle est d'utiliser simplement SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2']);

TProcess

Vous pouvez utiliser TProcess pour lancer un programme externe à votre application. Le principal intérêt d'utiliser TProcess est qu'il est :

  • Indépendant de la plateforme
  • Capable de lire les entrées sorties standards (stdin, stdout).

Un exemple simple

 // This is a demo program that shows how to launch
 // an external program.
 program launchprogram;
 
 // Here we include files that have useful functions
 // and procedures we will need.
 uses 
   Classes, SysUtils, Process;
 
 // This is defining the var "AProcess" as a variable 
 // of the type "TProcess"
 var 
   AProcess: TProcess;
 
 // This is where our program starts to run
 begin
   // Now we will create the TProcess object, and
   // assign it to the var AProcess.
   AProcess := TProcess.Create(nil);
 
   // Tell the new AProcess what the command to execute is.
   // Let's use the FreePascal compiler
   AProcess.CommandLine := 'ppc386 -h';
 
   // We will define an option for when the program
   // is run. This option will make sure that our program
   // does not continue until the program we will launch
   // has stopped running.                vvvvvvvvvvvvvv
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   // Now that AProcess knows what the commandline is 
   // we will run it.
   AProcess.Execute;
 
   // This is not reached until ppc386 stops running.
   AProcess.Free;   
 end.

C'est tout ! Vous venez d'apprendre à lancer un programme externe depuis votre application.

Un exemple amélioré

C'est bien gentil tout ça, mais comment puis je lire la sortie d'un programme que je viens de lancer ?

Bien, étendons juste un peu notre exemple :

 // Ceci est un programme de démonstration qui montre
 // comment lancer un programme externe et lire sa sortie
 program launchprogram;
 
 // Ici on inclue les fichiers qui ont les fonctions et procédures
 // dont nous avons besoin
 uses 
   Classes, SysUtils, Process;
 
 // Ceci définit la variable "AProcess" comment variable
 // de type "TProcess". On ajoute aussi une TStringList
  // pour stocker les données de la sortie programme.
 var 
   AProcess: TProcess;
   AStringList: TStringList;
 
 // Le programme commence ici.
 begin
   // On créé l'objet TProcess,
   // et on l'assigne à la variable AProcess
   AProcess := TProcess.Create(nil);
 
   // On créé l'objet TStringList
   AStringList := TStringList.Create;
 
   // On dit au nouveau AProcess quelle est la commande à exécuter
   // Utilisons le compilateur de FreePascal
   AProcess.CommandLine := 'ppc386 -h';
 
   // Définissons une option pour indiquer que le programme est lancé.
   // Cela assurera que notre programme n'attend pas que le programme lancé soit terminé.
   // Nous allons aussi lui dire que nous voulons lire la sortie du fichier.
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   // Maintenent que AProcess sait quelle ligne de commande il doit lancer, go!
   AProcess.Execute;
   
   // Le programme ne va pas plus loin tant que ppc386 n'a pas fini.
 
   // Maintenant on lit dans la TStringList la sortie du programme.
   AStringList.LoadFromStream(AProcess.Output);
   
   // Sauvegarde de la sortie dans un fichier
   AStringList.SaveToFile('output.txt');
 
   // Maintenant on peut vider la TStringList et le TProcess.
   AStringList.Free;
   AProcess.Free;   
 end.

Lire une grosse sortie

Dans l'exemple précédent, nous attendons que le programme se termine. Au moment de la lecture, le programme à écrit sur la sortie. Mais supposons que le programme écrive beaucoup d'informations en sortie, le canal se saturera et nécessitera une lecture. Mais le programme appelant ne peu rien lire tant que le programme appelé n'est pas terminé. Une étreinte mortelle (deadlock) se produit alors.

Le programme suivant, bien que n'utilisant pas poWaitOnExit, lit tout de même la sortie, tant que le programme est ouvert. La sortie est stockée dans un flux de mémoire, et peut être utilisé plus tard pour lire la sortie dans un TStringList.

 program procoutlarge;
 {
     Copyright (c) 2004 by Marc Weustink
 
     This example is creeated in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 }
 
 uses
   Classes, Process, SysUtils;
 
 const
   READ_BYTES = 2048;
   
 var
   S: TStringList;
   M: TMemoryStream;
   P: TProcess;
   n: LongInt;
   BytesRead: LongInt;
 
 begin
   // We cannot use poWaitOnExit here since we don't
   // know the size of the output. On Linux the size of the
   // output pipe is 2 kB. If the output data is more, we 
   // need to read the data. This isn't possible since we are 
   // waiting. So we get a deadlock here.
   //
   // A temp Memorystream is used to buffer the output
   
   M := TMemoryStream.Create;
   BytesRead := 0;
 
   P := TProcess.Create(nil);
   P.CommandLine := 'ppc386 -va bogus.pp';
   P.Options := [poUsePipes];
   WriteLn('-- executing --');
   P.Execute;
   while P.Running do
   begin          
     // make sure we have room
     M.SetSize(BytesRead + READ_BYTES);
     
     // try reading it
     n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
     if n > 0 
     then begin
       Inc(BytesRead, n);
       Write('.')
     end
     else begin     
       // no data, wait 100 ms
       Sleep(100); 
     end;
   end;
   // read last part
   repeat
     // make sure we have room
     M.SetSize(BytesRead + READ_BYTES);
     // try reading it
     n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
     if n > 0 
     then begin
       Inc(BytesRead, n);
       Write('.');
     end;
   until n <= 0;
   if BytesRead > 0 then WriteLn;
   M.SetSize(BytesRead); 
   WriteLn('-- executed --');
   
   S := TStringList.Create;
   S.LoadFromStream(M);
   WriteLn('-- linecount = ', S.Count, ' --');
   for n := 0 to S.Count - 1 do
   begin
     WriteLn('| ', S[n]);
   end;
   WriteLn('-- end --');
   S.Free;
   P.Free;
   M.Free;
 end.

Employer l'entrée et la sortie d'un TProcess

Voir l'exemple processdemo dans le dépôt SVN de Lazarus-CCR .

Remarque sur l'utilisation de TProcess

Si vous programmez une application multi-plateforme, vous pouvez changer la ligne de commande appelant l'application, en fonction du système d'exploitation. Pour cela utilisez {$IFDEF} et {$ENDIF}.

Exemple :

 {...}
   AProcess:TProcess.Create(nil)
   {$IFDEF WIN32}
   AProcess.CommandLine := 'calc.exe'; //Windows Calc
   {$ENDIF}
   {$IFDEF LINUX}
   AProcess.CommandLine := 'kcalc'; //KDE Calc
   {$ENDIF}
   AProcess.Execute; //in alternative, you can use AProcess.Active:=True
 {...}

Exemple de "Dialogue" avec le processus aspell

Dans le code source de pasdoc pasdoc vous trouverez deux unités qui analysent du texte par "dialogue" en lançant un processus aspell au travers de canaux :

  • PasDoc_ProcessLineTalk.pas unit implémente la classe TProcessLineTalk, descendante de TProcess, qui permet un dialogue facile entre n'importe quel processus sur le principe du ligne par ligne.
  • PasDoc_Aspell.pas units implémente la classe de TAspellProcess, qui permet d'analyser du texte en utilisant l'exemple de TProcessLineTalk pour exécuter aspell et communiquer avec le processus en cours d'exécution d'aspell.

Ces deux unités sont plutôt indépendantes du reste des sources de pasdoc, par conséquent elles peuvent servir d'exemples d'utilisation de TProcess en situation réelle pour exécuter et communiquer par les canaux avec un autre programme.