Executing External Programs/fr: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | + | ## Contents | |
| 2 | + | ||
| 3 | + | * 1 Introduction | |
| 4 | + | * 2 SysUtils.ExecuteProcess | |
| 5 | + | * 3 TProcess | |
| 6 | + | * 3.1 Un exemple simple | |
| 7 | + | * 3.2 Un exemple amélioré | |
| 8 | + | * 3.3 Lire une grosse sortie | |
| 9 | + | * 3.4 Employer l'entrée et la sortie d'un TProcess | |
| 10 | + | * 3.5 Remarque sur l'utilisation de TProcess | |
| 11 | + | * 3.6 Exemple de "Dialogue" avec le processus aspell | |
| 12 | + | ||
| 1 | 13 | ## Introduction | |
| 2 | 14 | ||
| 3 | - | ||
| 15 | + | Il y a différentes manière de lancer un programme externe, mais nous ne nous concentrerons que sur une seule : [TProcess](<http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html> "doc:fcl/process/tprocess.html"). | |
| 4 | 16 | ||
| 5 | - | ||
| 17 | + | 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). | |
| 6 | 18 | ||
| 7 | - | ||
| 19 | + | **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 ! | |
| 8 | 20 | ||
| 9 | 21 | ## SysUtils.ExecuteProcess | |
| 10 | 22 | ||
| 11 | - | ||
| 23 | + | 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']); | |
| 12 | 24 | ||
| 13 | 25 | ## TProcess | |
| 14 | 26 | ||
| 15 | - | ||
| 27 | + | Vous pouvez utiliser TProcess pour lancer un programme externe à votre application. Le principal intérêt d'utiliser TProcess est qu'il est : | |
| 16 | 28 | ||
| 17 | - | ||
| 18 | - | ||
| 29 | + | * Indépendant de la plateforme | |
| 30 | + | * Capable de lire les entrées sorties standards (stdin, stdout). | |
| 19 | 31 | ||
| 20 | 32 | ### Un exemple simple | |
| 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 | 33 | ``` | |
| 34 | + | // This is a demo program that shows how to launch | |
| 35 | + | // an external program. | |
| 36 | + | program launchprogram; | |
| 37 | + | ||
| 38 | + | // Here we include files that have useful functions | |
| 39 | + | // and procedures we will need. | |
| 40 | + | uses | |
| 41 | + | Classes, SysUtils, Process; | |
| 42 | + | ||
| 43 | + | // This is defining the var "AProcess" as a variable | |
| 44 | + | // of the type "TProcess" | |
| 45 | + | var | |
| 46 | + | AProcess: TProcess; | |
| 47 | + | ||
| 48 | + | // This is where our program starts to run | |
| 49 | + | begin | |
| 50 | + | // Now we will create the TProcess object, and | |
| 51 | + | // assign it to the var AProcess. | |
| 52 | + | AProcess := TProcess.Create(nil); | |
| 53 | + | ||
| 54 | + | // Tell the new AProcess what the command to execute is. | |
| 55 | + | // Let's use the FreePascal compiler | |
| 56 | + | AProcess.CommandLine := 'ppc386 -h'; | |
| 57 | + | ||
| 58 | + | // We will define an option for when the program | |
| 59 | + | // is run. This option will make sure that our program | |
| 60 | + | // does not continue until the program we will launch | |
| 61 | + | // has stopped running. vvvvvvvvvvvvvv | |
| 62 | + | AProcess.Options := AProcess.Options + [poWaitOnExit]; | |
| 63 | + | ||
| 64 | + | // Now that AProcess knows what the commandline is | |
| 65 | + | // we will run it. | |
| 66 | + | AProcess.Execute; | |
| 67 | + | ||
| 68 | + | // This is not reached until ppc386 stops running. | |
| 69 | + | AProcess.Free; | |
| 70 | + | end. | |
| 71 | + | ``` | |
| 61 | 72 | ||
| 62 | - | ||
| 73 | + | C'est tout ! Vous venez d'apprendre à lancer un programme externe depuis votre application. | |
| 63 | 74 | ||
| 64 | 75 | ### Un exemple amélioré | |
| 65 | 76 | ||
| 66 | - | ||
| 67 | - | ||
| 68 | - | ||
| 77 | + | C'est bien gentil tout ça, mais comment puis je lire la sortie d'un programme que je viens de lancer ? | |
| 69 | 78 | ||
| 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 | - | ||
| 79 | + | Bien, étendons juste un peu notre exemple : | |
| 120 | 80 | ``` | |
| 81 | + | // Ceci est un programme de démonstration qui montre | |
| 82 | + | // comment lancer un programme externe et lire sa sortie | |
| 83 | + | program launchprogram; | |
| 84 | + | ||
| 85 | + | // Ici on inclue les fichiers qui ont les fonctions et procédures | |
| 86 | + | // dont nous avons besoin | |
| 87 | + | uses | |
| 88 | + | Classes, SysUtils, Process; | |
| 89 | + | ||
| 90 | + | // Ceci définit la variable "AProcess" comment variable | |
| 91 | + | // de type "TProcess". On ajoute aussi une TStringList | |
| 92 | + | // pour stocker les données de la sortie programme. | |
| 93 | + | var | |
| 94 | + | AProcess: TProcess; | |
| 95 | + | AStringList: TStringList; | |
| 96 | + | ||
| 97 | + | // Le programme commence ici. | |
| 98 | + | begin | |
| 99 | + | // On créé l'objet TProcess, | |
| 100 | + | // et on l'assigne à la variable AProcess | |
| 101 | + | AProcess := TProcess.Create(nil); | |
| 102 | + | ||
| 103 | + | // On créé l'objet TStringList | |
| 104 | + | AStringList := TStringList.Create; | |
| 105 | + | ||
| 106 | + | // On dit au nouveau AProcess quelle est la commande à exécuter | |
| 107 | + | // Utilisons le compilateur de FreePascal | |
| 108 | + | AProcess.CommandLine := 'ppc386 -h'; | |
| 109 | + | ||
| 110 | + | // Définissons une option pour indiquer que le programme est lancé. | |
| 111 | + | // Cela assurera que notre programme n'attend pas que le programme lancé soit terminé. | |
| 112 | + | // Nous allons aussi lui dire que nous voulons lire la sortie du fichier. | |
| 113 | + | AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes]; | |
| 114 | + | ||
| 115 | + | // Maintenent que AProcess sait quelle ligne de commande il doit lancer, go! | |
| 116 | + | AProcess.Execute; | |
| 117 | + | ||
| 118 | + | // Le programme ne va pas plus loin tant que ppc386 n'a pas fini. | |
| 119 | + | ||
| 120 | + | // Maintenant on lit dans la TStringList la sortie du programme. | |
| 121 | + | AStringList.LoadFromStream(AProcess.Output); | |
| 122 | + | ||
| 123 | + | // Sauvegarde de la sortie dans un fichier | |
| 124 | + | AStringList.SaveToFile('output.txt'); | |
| 125 | + | ||
| 126 | + | // Maintenant on peut vider la TStringList et le TProcess. | |
| 127 | + | AStringList.Free; | |
| 128 | + | AProcess.Free; | |
| 129 | + | end. | |
| 130 | + | ``` | |
| 121 | 131 | ||
| 122 | 132 | ### Lire une grosse sortie | |
| 123 | 133 | ||
| 124 | - | ||
| 125 | - | ||
| 126 | - | ||
| 134 | + | 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. | |
| 127 | 135 | ||
| 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 | - | ||
| 153 | - | ||
| 154 | - | ||
| 155 | - | ||
| 156 | - | ||
| 157 | - | ||
| 158 | - | ||
| 159 | - | ||
| 160 | - | ||
| 161 | - | ||
| 162 | - | ||
| 163 | - | ||
| 164 | - | ||
| 165 | - | ||
| 166 | - | ||
| 167 | - | ||
| 168 | - | ||
| 169 | - | ||
| 170 | - | ||
| 171 | - | ||
| 136 | + | 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. | |
| 137 | + | ``` | |
| 138 | + | program procoutlarge; | |
| 139 | + | { | |
| 140 | + | Copyright (c) 2004 by Marc Weustink | |
| 172 | 141 | ||
| 173 | - | ||
| 174 | - | ||
| 175 | - | ||
| 176 | - | ||
| 177 | - | ||
| 178 | - | ||
| 179 | - | ||
| 180 | - | ||
| 181 | - | ||
| 182 | - | ||
| 183 | - | ||
| 184 | - | ||
| 185 | - | ||
| 186 | - | ||
| 187 | - | ||
| 188 | - | ||
| 189 | - | ||
| 190 | - | ||
| 191 | - | ||
| 192 | - | ||
| 193 | - | ||
| 194 | - | ||
| 195 | - | ||
| 196 | - | ||
| 197 | - | ||
| 198 | - | ||
| 199 | - | ||
| 200 | - | ||
| 201 | - | ||
| 202 | - | ||
| 203 | - | ||
| 204 | - | ||
| 205 | - | ||
| 206 | - | ||
| 207 | - | ||
| 208 | - | ||
| 209 | - | ||
| 210 | - | ||
| 211 | - | ||
| 212 | - | ||
| 142 | + | This example is creeated in the hope that it will be useful, | |
| 143 | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 144 | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | uses | |
| 148 | + | Classes, Process, SysUtils; | |
| 149 | + | ||
| 150 | + | const | |
| 151 | + | READ_BYTES = 2048; | |
| 152 | + | ||
| 153 | + | var | |
| 154 | + | S: TStringList; | |
| 155 | + | M: TMemoryStream; | |
| 156 | + | P: TProcess; | |
| 157 | + | n: LongInt; | |
| 158 | + | BytesRead: LongInt; | |
| 159 | + | ||
| 160 | + | begin | |
| 161 | + | // We cannot use poWaitOnExit here since we don't | |
| 162 | + | // know the size of the output. On Linux the size of the | |
| 163 | + | // output pipe is 2 kB. If the output data is more, we | |
| 164 | + | // need to read the data. This isn't possible since we are | |
| 165 | + | // waiting. So we get a deadlock here. | |
| 166 | + | // | |
| 167 | + | // A temp Memorystream is used to buffer the output | |
| 168 | + | ||
| 169 | + | M := TMemoryStream.Create; | |
| 170 | + | BytesRead := 0; | |
| 171 | + | ||
| 172 | + | P := TProcess.Create(nil); | |
| 173 | + | P.CommandLine := 'ppc386 -va bogus.pp'; | |
| 174 | + | P.Options := [poUsePipes]; | |
| 175 | + | WriteLn('-- executing --'); | |
| 176 | + | P.Execute; | |
| 177 | + | while P.Running do | |
| 178 | + | begin | |
| 179 | + | // make sure we have room | |
| 180 | + | M.SetSize(BytesRead + READ_BYTES); | |
| 181 | + | ||
| 182 | + | // try reading it | |
| 183 | + | n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES); | |
| 184 | + | if n > 0 | |
| 185 | + | then begin | |
| 186 | + | Inc(BytesRead, n); | |
| 187 | + | Write('.') | |
| 188 | + | end | |
| 189 | + | else begin | |
| 190 | + | // no data, wait 100 ms | |
| 191 | + | Sleep(100); | |
| 192 | + | end; | |
| 193 | + | end; | |
| 194 | + | // read last part | |
| 195 | + | repeat | |
| 196 | + | // make sure we have room | |
| 197 | + | M.SetSize(BytesRead + READ_BYTES); | |
| 198 | + | // try reading it | |
| 199 | + | n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES); | |
| 200 | + | if n > 0 | |
| 201 | + | then begin | |
| 202 | + | Inc(BytesRead, n); | |
| 203 | + | Write('.'); | |
| 204 | + | end; | |
| 205 | + | until n <= 0; | |
| 206 | + | if BytesRead > 0 then WriteLn; | |
| 207 | + | M.SetSize(BytesRead); | |
| 208 | + | WriteLn('-- executed --'); | |
| 209 | + | ||
| 210 | + | S := TStringList.Create; | |
| 211 | + | S.LoadFromStream(M); | |
| 212 | + | WriteLn('-- linecount = ', S.Count, ' --'); | |
| 213 | + | for n := 0 to S.Count - 1 do | |
| 214 | + | begin | |
| 215 | + | WriteLn('| ', S[n]); | |
| 216 | + | end; | |
| 217 | + | WriteLn('-- end --'); | |
| 218 | + | S.Free; | |
| 219 | + | P.Free; | |
| 220 | + | M.Free; | |
| 221 | + | end. | |
| 213 | 222 | ``` | |
| 214 | 223 | ||
| 215 | 224 | ### Employer l'entrée et la sortie d'un TProcess | |
| 216 | 225 | ||
| 217 | - | ||
| 226 | + | Voir l'exemple processdemo dans le [dépôt SVN de Lazarus-CCR ](<https://svn.sourceforge.net/svnroot/lazarus-ccr/examples/process>). | |
| 218 | 227 | ||
| 219 | 228 | ### Remarque sur l'utilisation de TProcess | |
| 220 | 229 | ||
| 221 | - | ||
| 222 | - | ||
| 223 | - | ||
| 230 | + | 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}. | |
| 224 | 231 | ||
| 225 | - | ||
| 226 | - | ||
| 227 | - | ||
| 228 | - | ||
| 229 | - | ||
| 230 | - | ||
| 231 | - | ||
| 232 | - | ||
| 233 | - | ||
| 234 | - | ||
| 235 | - | ||
| 232 | + | Exemple : | |
| 236 | 233 | ``` | |
| 234 | + | {...} | |
| 235 | + | AProcess:TProcess.Create(nil) | |
| 236 | + | {$IFDEF WIN32} | |
| 237 | + | AProcess.CommandLine := 'calc.exe'; //Windows Calc | |
| 238 | + | {$ENDIF} | |
| 239 | + | {$IFDEF LINUX} | |
| 240 | + | AProcess.CommandLine := 'kcalc'; //KDE Calc | |
| 241 | + | {$ENDIF} | |
| 242 | + | AProcess.Execute; //in alternative, you can use AProcess.Active:=True | |
| 243 | + | {...} | |
| 244 | + | ``` | |
| 237 | 245 | ||
| 238 | 246 | ### Exemple de "Dialogue" avec le processus aspell | |
| 239 | 247 | ||
| 240 | - | ||
| 248 | + | Dans le code source de pasdoc [pasdoc](<http://pasdoc.sourceforge.net/>) vous trouverez deux unités qui analysent du texte par "dialogue" en lançant un processus aspell au travers de canaux : | |
| 241 | 249 | ||
| 242 | - | ||
| 250 | + | * [PasDoc_ProcessLineTalk.pas unit](<http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_ProcessLineTalk.pas>) 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. | |
| 243 | 251 | ||
| 244 | - | ||
| 252 | + | * [PasDoc_Aspell.pas units](<http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_Aspell.pas>) 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. | |
| 245 | 253 | ||
| 246 | 254 | 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. | |
| 247 | 255 | ||
| 248 | 256 | --- | |
| 249 | - | ||
| 257 | + | *From the Free Pascal wiki, page "Executing External Programs/fr", as the Internet Archive kept it on 2023-03-22. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
