Executing External Programs/es
│
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) │
Introducción
Hay varias formas de ejecutar programas externos, pero nos centraremos en una, TProcess.
Si en Delphi utilizaba siempre ShellExecute y/o WinExec, puede empezar a usar TProcess como alternativa en FPC/Lazarus (es válido también si está ejecutando Lazarus en Linux, porque TProcess es plataforma cruzada).
Nota: FPC/Lazarus puede utilizar ShellExecute y/o WinExec, pero sólo en Win32. Si su programa es de plataforma cruzada, utilice TProcess, ¡es mejor!.
TProcess
Puede utilizar TProcess para ejecutar programas externos. Algunas de las ventajas son:
- Independiente de la plataforma
- Capaz de leer de stdout y escribir a stdin.
Un ejemplo sencillo
// Esto es un programa de demostración que enseña como lanzar // un programa externo. program launchprogram; // Aquí incluimos archivos que tienen funciones y procedimientos // útiles que necesitaremos. uses Classes, SysUtils, Process; // Definición de "AProcess" como una variable // de tipo "TProcess" var AProcess: TProcess; // Aquí comienza a ejecutarse nuestro programa begin // Ahora se creará el objeto TProcess, y // lo asignamos a la variable AProcess. AProcess := TProcess.Create(nil); // Le decimos al nuevo AProcess la orden que debe ejecutar. // Vamos a utilizar el compilador FreePascal AProcess.CommandLine := 'ppc386 -h'; // Definiremos una opción para cuando se ejecute // el programa. Esta opción hará que nuestro programa // no continúe hasta que el programa que lancemos // haya terminado de ejecutarse. vvvvvvvvvvvvvv AProcess.Options := AProcess.Options + [poWaitOnExit]; // Ahora que AProcess conoce cuál es la orden, // lo ejecutamos. AProcess.Execute; // Esto ocurrirá cuando ppc386 termine de ejecutarse. AProcess.Free; end.
¡Eso es todo! Acaba de aprender cómo se ejecuta un programa externo desde dentro de su propio programa.
Un ejemplo mejorado
De acuerdo, pero ¿cómo leo la salida de un programa que he ejecutado?
Bien, vamos a ampliar un poco nuestro ejemplo mediante lo siguiente:
// Esto es un programa de demostración que enseña como lanzar // un programa externo y leer su salida. program launchprogram; // Aquí incluimos archivos que tienen funciones y procedimientos // útiles que necesitaremos. uses Classes, SysUtils, Process; // Definición de "AProcess" como una variable // de tipo "TProcess" // También añadimos un TStringList para almacenar los datos // leídos desde la salida del programa. var AProcess: TProcess; AStringList: TStringList; // Aquí comienza a ejecutarse nuestro programa. begin // Ahora crearemos el objeto TProcess, y // lo asignamos a la variable AProcess. AProcess := TProcess.Create(nil); // Creamos el objeto TStringList. AStringList := TStringList.Create; // Le decimos al nuevo AProcess la orden que debe ejecutar. // Vamos a utilizar el compilador FreePascal. AProcess.CommandLine := 'ppc386 -h'; // Definiremos una opción para cuando se ejecute // el programa. Esta opción hará que nuestro programa // no continúe hasta que el programa que ejecutemos // termine. Ahora también le diremos que // queremos leer la salida del archivo. AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes]; // Ahora que AProcess conoce la orden, lo ejecutamos. AProcess.Execute; // Esto ocurrirá cuando ppc386 termine de ejecutarse. // Ahora leemos la salida del programa que acabamos de ejecutar // dentro de TStringList. AStringList.LoadFromStream(AProcess.Output); // Guardamos la salida en un archivo. AStringList.SaveToFile('output.txt'); // Ahora que hemos guardado el archivo podemos liberar de la memoria // TStringList y TProcess. AStringList.Free; AProcess.Free; end.
Lectura de muchos datos de salida
En el ejemplo anterior esperábamos a que el programa terminara. En ese momento leíamos lo que el programa había escrito en su salida. Pero si el programa escribe muchos datos, la tubería se llena, será necesario leer, pero el programa que llama no leerá, hasta que el programa llamado haya terminado. Nos encontraremos en un punto muerto.
El siguiente ejemplo, por consiguiente, no utiliza poWaitOnExit, sino que lee la salida mientras que el programa se está ejecutando. La salida se almacena en la memoria intermedia, que se puede utilizar más tarde para leerla en un TStringList.
program procoutlarge; { Copyright (c) 2004 by Marc Weustink Este ejemplo se ha creado con la esperanza de que resulte útil, pero SIN NINGUNA GARANTÍA; ni tan siquiera la garantía implícita de COMERCIALIZACIÓN o ADECUACIÓN A UN OBJETIVO PARTICULAR. } uses Classes, Process, SysUtils; const READ_BYTES = 2048; var S: TStringList; M: TMemoryStream; P: TProcess; n: LongInt; BytesRead: LongInt; begin // No podemos utilizar poWaitOnExit porquue no // conocemos el tamaño de la salida. En Linux el tamaño // de la salida de la tubería es de 2 kB. Si los datos de salida son más // necesitamos leerlos. Esto no es posible, ya que estamos // esperando. Así que aquí nos encontramos en un punto muerto. // // Se utiliza un Memorystream temporal para almacenar la salida 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 // nos aseguramos de tener espacio M.SetSize(BytesRead + READ_BYTES); // intentamos leerlo n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES); if n > 0 then begin Inc(BytesRead, n); Write('.') end else begin // sin datos, esperamos 100 milisegundos Sleep(100); end; end; // leemos la última parte repeat // nos aseguramos de tener espacio M.SetSize(BytesRead + READ_BYTES); // intentamos leerlo 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.
Notas sobre la utilización de TProcess
Si está creando un programa de plataforma cruzada, puede cambiar la orden de acuerdo con el sistema operativo, mediante las directivas "{$IFDEF}s" y "{$ENDIF}s".
Ejemplo:
{...} 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 {...}
Ejemplo de "talking" con el proceso aspell
Dentro del código fuente pasdoc puede encontrar dos unidades que realizan corrección ortográfica mediante "talking" con ejecución del proceso aspell a través de tuberías:
- PasDoc_ProcessLineTalk.pas unit ejecuta la clase TProcessLineTalk, descendiente de TProcess, que se puede utilizar fácilmente para comunicar con cualquier proceso línea a línea.
- PasDoc_Aspell.pas units ejecuta la clase TAspellProcess, que realiza corrección ortográfica utilizando la instancia fundamental TProcessLineTalk para ejecutar aspell y comunicarse con el proceso aspell que se está ejecutando.
Ambas unidades son bastante independientes del resto de fuentes de pasdoc, así que pueden servir como ejemplos del mundo real de la utilización de TProcess para ejecutar y comunicarse a través de tuberías con otros programas.