Executing External Programs/it

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)

Introduzione

Ci sono molti modi di eseguire un programma esterno, ma questo articolo si focalizza solo su uno: TProcess. Se usi solitamente ShellExecute e / o WinExec in Delphi, allora puoi iniziare ad usare TProcess come un'alternativa in FPC/Lazarus (Questo vale anche se usi usi Lazarus in Linux, perchè TProcess è cross-platform).

Nota: FPC/Lazarus supporta ShellExecute e / o WinExec, ma questo supporto è solo in Win32. Se vuoi programmare in 'cross-platform', allora usa TProcess, è la miglior scelta!


SysUtils.ExecuteProcess

Il modo più semplice se non necessiti pipes ne ogni forma di controllo è di usare semplicemente SysUtils.ExecuteProcess('/percorso/completo/al/file/binario',['arg1','arg2']);


TProcess

Puoi usare TProcess per lanciare programmi esterni.Alcuni benefici nell'uso di TProcess sono:

  • Piattaforma Indipendente
  • Capacità di leggere da stdout e scrivere in stdin.

Nota: TProcess non è un terminal/shell! Non puoi eseguire direttamente scripts or redirigere output usando operatori tipo "|", ">", "<", "&" etc. Eì possibile ottenere gli stessi risultati con TProcess usando Pascal, qualche sempio è di seguito..

Importante: Devi specificare il percorso completo all'eseguibile. Per esempio '/bin/cp' invece di 'cp'. Se il programma è nel percorso standard PATH allora puoi usare la funzione FindDefaultExecutablePath da FileUtil unit di LCL.

Un semplice esempio

<pascal>

// Questo è un programma dimostrativo che mostra come lanciare
// un mprogramma esterno.
program launchprogram;

// Qui includiamo i files che hanno utilizzo funzionale
// e le procedure che ci occorreranno.
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.

</pascal> Questo è quanto! Hai appena imparato come eseguire un programma esterno dall'interno del tuo stesso programma.

Un esempio comprovato

Che bello!, ma come leggo l' Output di un programma che ho eseguito ?

Bene, espandiamo il nostro esempio cosi che otteniamo questo:

<delphi>

// This is a demo program that shows how to launch
// an external program and read from it's output.
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"
// Also now we are adding a TStringList to store the 
// data read from the programs output.
var 
  AProcess: TProcess;
  AStringList: TStringList;

// 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);

  // Create the TStringList object.
  AStringList := TStringList.Create;

  // 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. Also now we will tell it that
  // we want to read the output of the file.
  AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];

  // Now that AProcess knows what the commandline is 
  // we will run it.
  AProcess.Execute;
  
  // This is not reached until ppc386 stops running.

  // Now read the output of the program we just ran
  // into the TStringList.
  AStringList.LoadFromStream(AProcess.Output);
  
  // Save the output to a file.
  AStringList.SaveToFile('output.txt');

  // Now that the file is saved we can free the 
  // TStringList and the TProcess.
  AStringList.Free;
  AProcess.Free;   
end.

</delphi>

Leggere un grande output

Nel precedente esempio noi abbiamo aspettato sinché il programma non è terminato. Ora noi leggiamo, quello che il programma ha scritto nel suo output. Ma supponiamo che il programma scriva molti dati nell'output, il buffer si riempie e il programma richiamato aspetta sinché il buffer non viene letto. MA il programma chiamante non legge dal buffer, sinché il programma chiamato non ha terminato. Avviene un blocco.

Il seguente esempio tuttavia non usa poWaitOnExit, ma legge dall'output, mentre il programma è ancora in esecuzione. L'output è immagazzinato in un buffer di memoria, che potrà essere usato più tardi per leggere l'output in TStringList.

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

Usare input e output di TProcess

Vedi l'esempio dimostrativo in Lazarus-CCR SVN.

Accorgimenti nell'uso di TProcess

Se stai creando un programma multipiattaforma (cross-platform) , Puoi cambiare la linea di comando in base al Sistema Operativo , usando le direttive "{$IFDEF}s" and "{$ENDIF}s".

Esempio: <delphi> {...}

  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
{...}</delphi>

Esempio di dialogo con il processo aspell

All'interno di pasdoc Codice sorgente puoi trovare due units che fanno spell-checking mediante "dialogo" con aspell in esecuzione attraverso pipes:

  • PasDoc_ProcessLineTalk.pas unit implements TProcessLineTalk class, descendant of TProcess, that can be easily used to talk with any process on a line-by-line basis.
  • PasDoc_Aspell.pas units implements TAspellProcess class, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.

Entrambe le units sono indipendenti dal resto di pasdoc sorgente, cosi che esse possono servire come esempio reale dell'uso di TProcess per eseguire e comunicare attraverso pipes con altri programmi.


TXMLPropStorage