Sign in Join
Wiki copytemporary

Executing External Programs/es: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introducción
22
33   Hay varias formas de ejecutar programas externos, pero nos centraremos en una, **TProcess**.
44
55Si en Delphi utilizabas siempre **ShellExecute** y/o **WinExec**, puedes empezar a usar TProcess como alternativa en FPC/Lazarus (es válido también si estás ejecutando Lazarus en Linux, porque TProcess es multiplataforma).
66
77   **Nota:** FPC/Lazarus puede utilizar **ShellExecute** y/o **WinExec**, pero sólo en Win32. Si tu programa está enfocado a varias plataformas, utiliza TProcess, ¡es mejor!.
88
99Aquí tenemos las diferentes maneras disponibles sobre como ejecutar un programa/proceso/mandato externo en RTL, FCL y LCL.
1010
1111| Metodo | Librería | Plataformas | Línea simple | Características |
1212|----|----|----|----|----|
1313| [[Executing External Programs|ExecuteProcess]] | RTL | Plataforma Cruzada | Si | Muy limitado, synchronous |
1414| [[Executing External Programs|ShellExecute]] | WinAPI | MS Windows only | Yes | Muchos. Permite iniciar programas con permisos de elevación/admin. |
1515| [[Executing External Programs|fpsystem, fpexecve]] | Unix | Unix solamente | | |
1616| [[Executing External Programs/es|TProcess]] | FCL | Plataforma cruzada | No | Completas |
1717| [[Executing External Programs|RunCommand]] | FCL | Plataforma cruzada, **Requiere FPC 2.6.2+** | Si | Cubre el uso común de TProcess |
1818| [[Executing External Programs|OpenDocument]] | LCL | Plataforma cruzada | si | Solo para abrir documentos. El documento debe abrirse con la aplicación asociada al tipo de documento. |
1919
2020## TProcess
2121
2222   Puedes utilizar TProcess para ejecutar programas externos. Algunas de sus ventajas son:
2323
2424- Independiente de la plataforma
2525- Capaz de leer desde la entrada estándar (*stdout*) y de escribir en la salida estándar (*stdin*).
2626
2727   **Nota**: TProcess no es un terminal de shell! No puedes ejecutar directamente secuencias de comandos o reorientar la salida usando operadores como "\|", "\>", "\<", "&", etc. Es posible obtener los mismos resultados con TProcess utilizando Pascal, mira algunos ejemplos más abajo..
2828
2929   **Importante**: Debes especificar la ruta completa al ejecutable. Por ejemplo "/bin/cp " en lugar de "cp". Si el programa está en la ruta estándar, puedes utilizar la función **FindDefaultExecutablePath** de la unidad **FileUtil** de la LCL.
3030
3131### Un ejemplo sencillo
3232
3333``` pascal
3434 // Este es un programa de demostración que enseña como lanzar
3535 // un programa externo.
3636 program lanzarprograma;
3737
3838 uses
3939 Classes, SysUtils, Process;
4040
4141 // Definición de "UnProceso" como una variable
4242 // de tipo "TProcess"
4343 var
4444 UnProceso: TProcess;
4545
4646 begin
4747 // Ahora creamos UnProceso.
4848 UnProceso := TProcess.Create(nil);
4949
5050 // Asignamos a UnProceso la orden que debe ejecutar.
5151 // Vamos a lanzar el compilador de FreePascal
5252 UnProceso.CommandLine := 'ppc386 -h';
5353
5454 // Definimos una opción de comportamiento de 'TProccess'
5555 // La opción poWaitOnExit hará que nuestro programa
5656 // se detenga hasta que termine el programa lanzado
5757 UnProceso.Options := UnProceso.Options + [poWaitOnExit];
5858
5959 // Lanzamos la ejecución de 'ppc386 -h'.
6060 UnProceso.Execute;
6161
6262 // Nuestro programa espera hasta que 'ppc386' finaliza.
6363 UnProceso.Free;
6464 end.
6565```
6666
6767   ¡Eso es todo! Acabas de aprender cómo se ejecuta un programa externo desde dentro de tu propio programa.
6868
6969### Un ejemplo mejorado
7070
7171   De acuerdo, pero ¿cómo leo la salida que produce un programa que he ejecutado?
7272
7373   Bien, vamos a ampliar un poco nuestro ejemplo mediante lo siguiente:
7474
7575``` pascal
7676 program lanzarprograma2;
7777
7878 uses
7979 Classes, SysUtils, Process;
8080
8181 // Definición de "UnProceso" como una variable de tipo "TProcess"
8282 // También añadimos un TStringList para almacenar los datos
8383 // leídos desde la salida del programa.
8484 var
8585 UnProceso: TProcess;
8686 UnaLista: TStringList;
8787
8888 // Aquí comienza a ejecutarse nuestro programa.
8989 begin
9090
9191 // Creamos el objeto TStringList.
9292 UnaLista := TStringList.Create;
9393
9494 // Ahora creamos UnProceso.
9595 UnProceso := TProcess.Create(nil);
9696
9797 // Asignamos a UnProceso la orden que debe ejecutar.
9898 // Vamos a lanzazar el compilador de FreePascal
9999 // (CommandLine está Obsoleto)
100100 UnProceso.CommandLine := 'ppc386 -h';
101101
102102 // Definimos una opción de comportamiento de 'TProccess'
103103 // La opción poWaitOnExit hará que nuestro programa
104104 // se detenga hasta que termine el programa lanzado
105105 // con la opción poUsePipes advertimos que vamos a utilizar tuberías de Entrada/Salida
106106 // para que se capture la salida y así poder recuperar su contenido desde nuestro programa
107107
108108 UnProceso.Options := UnProceso.Options + [poWaitOnExit, poUsePipes];
109109
110110 // Lanzamos la ejecución de 'ppc386 -h'.
111111 UnProceso.Execute;
112112
113113 // Nuestro programa se detiene hasta que 'ppc386' finaliza.
114114
115115 // Ahora leemos la salida del programa que acabamos de ejecutar
116116 // dentro de TStringList.
117117 UnaLista.LoadFromStream(UnProceso.Output);
118118
119119 // Guardamos la salida en un archivo.
120120 UnaLista.SaveToFile('salida.txt');
121121
122122 // Ahora que hemos guardado el archivo podemos liberar la memoria
123123 // TStringList y TProcess.
124124 UnaLista.Free;
125125 UnProceso.Free;
126126 end.
127127```
128128
129129### Lectura de muchos datos de salida
130130
131131   En el ejemplo anterior esperamos a que el programa termine. En ese momento leemos lo que el programa ha escrito en la salida. Pero si el programa escribe muchos datos, la tubería se llena y será necesario leerlos para vaciarla, pero el programa llamante no leerá hasta que el programa llamado haya terminado. Nos encontraremos ante un típico *bloqueo de la muerte*.
132132
133133   El siguiente ejemplo, por tanto, no utiliza poWaitOnExit, sino que va leyendo la salida mientras el programa llamado se está ejecutando. La salida se almacena en una memoria intermedia, que se utilizará más tarde para leer en un TStringList, por ejemplo.
134134
135135``` pascal
136136 program procoutlarge;
137137 { Copyright (c) 2004 by Marc Weustink
138138
139139 Este ejemplo se ha creado con la esperanza de que resulte útil,
140140 pero SIN NINGUNA GARANTÍA; ni tan siquiera la garantía implícita de
141141 COMERCIALIZACIÓN o ADECUACIÓN A UN OBJETIVO PARTICULAR.
142142 }
143143
144144 uses
145145 Classes, Process, SysUtils;
146146
147147 const
148148 READ_BYTES = 2048;
149149
150150 var
151151 S: TStringList;
152152 M: TMemoryStream;
153153 P: TProcess;
154154 n: LongInt;
155155 BytesRead: LongInt;
156156
157157 begin
158158 // No podemos utilizar poWaitOnExit porque no
159159 // conocemos el tamaño de la salida. En Linux el tamaño
160160 // de la salida de la tubería es de 2 kB. Si los datos de salida son más
161161 // necesitamos leerlos. Esto no es posible, ya que estamos
162162 // esperando. Así que aquí nos encontramos en un punto muerto.
163163 //
164164 // Se utiliza un Memorystream temporal para almacenar la salida
165165
166166 M := TMemoryStream.Create;
167167 BytesRead := 0;
168168
169169 P := TProcess.Create(nil);
170170 P.CommandLine := 'ppc386 -va bogus.pp';
171171 P.Options := [poUsePipes];
172172 WriteLn('-- ejecutando --');
173173 P.Execute;
174174 while P.Running do
175175 begin
176176 // nos aseguramos de tener espacio
177177 M.SetSize(BytesRead + READ_BYTES);
178178
179179 // intentamos leerlo
180180 n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
181181 if n > 0
182182 then begin
183183 Inc(BytesRead, n);
184184 Write('.')
185185 end
186186 else begin
187187 // sin datos, esperamos 100 milisegundos
188188 Sleep(100);
189189 end;
190190 end;
191191 // leemos la última parte
192192 repeat
193193 // nos aseguramos de tener espacio
194194 M.SetSize(BytesRead + READ_BYTES);
195195 // intentamos leerlo
196196 n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
197197 if n > 0
198198 then begin
199199 Inc(BytesRead, n);
200200 Write('.');
201201 end;
202202 until n <= 0;
203203 if BytesRead > 0 then WriteLn;
204204 M.SetSize(BytesRead);
205205 WriteLn('-- ejecutado --');
206206
207207 S := TStringList.Create;
208208 S.LoadFromStream(M);
209209 WriteLn('-- total de líneas = ', S.Count, ' --');
210210 for n := 0 to S.Count - 1 do
211211 begin
212212 WriteLn('| ', S[n]);
213213 end;
214214 WriteLn('-- fin --');
215215 S.Free;
216216 P.Free;
217217 M.Free;
218218 end.
219219```
220220
221221### Uso de entrada y salida de un TProcess
222222
223223   Véase el ejemplo processdemo en [Lazarus-CCR SVN](https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process)
224224
225225### Notas sobre la utilización de TProcess
226226
227227   Si estas creando un programa que funciones en diversas plataformas, puedes cambiar el programa a ejecutar de acuerdo con el sistema operativo, mediante las directivas *{\$IFDEF}* y *{\$ENDIF}*.
228228
229229   Ejemplo:
230230
231231``` pascal
232232 {...}
233233 unProceso:TProcess.Create(nil)
234234 {$IFDEF WIN32}
235235 unProceso.CommandLine := 'calc.exe'; //calculadora de Windows
236236 {$ENDIF}
237237 {$IFDEF LINUX}
238238 unProceso.CommandLine := 'kcalc'; //calculadora de KDE o bien xcalc si no tienes KDE
239239 {$ENDIF}
240240 unProceso.Execute; //también puedes utilizar unProceso.Active:=True
241241 {...}
242242```
243243
244244En cuanto a TProcess tomando como referencia las propiedades del componente visual tiene las siguientes opciones (po=process options):
245245
246246- poDebugOnlyThisProcess.
247247- poDebugProcess.
248248- poDefaultErrorMode
249249- poNewConsole.
250250- poNewProcessGroup.
251251- poNoConsole.
252252- poRunSuspended.
253253- poStderrToOutPut.
254254- poUsePipes: utilizado para interconectar procesos.
255255- poWaitOnExit: esperar a que finalice el programa.
256256
257-<img src="TProcess_Options.PNG" title="TProcess_Options.PNG" width="300" />
257+![](/wiki-images/6/6e/TProcess_Options.PNG)
258258
259259A falta de explicar que hace cada opción.
260260
261261[Referencia TProcess](http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html).
262262
263263### Ejemplo de *conversación* con el proceso aspell
264264
265265   Dentro del código fuente [pasdoc](http://pasdoc.sourceforge.net/) puedes encontrar dos unidades que realizan corrección ortográfica *conversando* con un proceso aspell a través de tuberías:
266266
267267- [PasDoc_ProcessLineTalk.pas unit](http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_ProcessLineTalk.pas) ejecuta la clase TProcessLineTalk, descendiente de TProcess, que se puede utilizar fácilmente para comunicar con cualquier proceso línea a línea.
268268
269269- [PasDoc_Aspell.pas units](http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_Aspell.pas) 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.
270270
271271   Ambas unidades son bastante independientes del resto de fuentes de pasdoc, así que pueden servir como ejemplos reales de la utilización de TProcess para ejecutar y comunicarse a través de tuberías con otros programas.
272272
273273### Reemplazar operadores de *Shell* como "\|", "\<" y "\>"
274274
275275   A veces queremos ejecutar una orden compleja que envía (entuba) sus datos a otra orden o a un archivo. Algo como
276276
277277` ShellExecute('primeraorden.exe | segundaorden.exe');`
278278
279279o
280280
281281` ShellExecute('dir > salida.txt');`
282282
283283    Esto no funciona con TProcess. p.e.:
284284
285285` // esto no funciona`\
286286` Process.CommandLine := 'primeraorden.exe | segundaorden.exe'; `\
287287` Process.Execute;`
288288
289289#### ¿Por qué utilizar operadores especiales para redirigir la salida no funciona?
290290
291291   TProcess es justamente, eso, un proceso, no es un entorno de *shell*. No es dos procesos, únicamente es un proceso. Es posible, no obstante, redirigir la salida de la manera que queremos. Ver [[Executing External Programs/es|más adelante, la siguiente sección]].
292292
293293### Cómo redirigir la salida con TProcess
294294
295295   Podemos redirigir la salidad de un comando a otro comando utilizando una instancia de TProcess para cada commando.
296296
297297   Este es un ejemplo que explica cómo redirigir la salida de un proceso a otro. Para redirigir la salida de un proceso a un archivo o flujo ver el ejemplo [[Executing External Programs/es|Lectura de muchos datos de salida]]
298298
299299``` pascal
300300 program Proyecto1;
301301
302302 uses
303303 Classes, sysutils, process;
304304
305305 var
306306 PrimerProceso,
307307 SegundoProceso: TProcess;
308308 Buffer: array[0..127] of char;
309309 ContadorLectura: Integer;
310310 TamanyoLectura: Integer;
311311 begin
312312 PrimerProceso := TProcess.Create(nil);
313313 SegundoProceso := TProcess.Create(nil);
314314
315315 PrimerProceso.Options := [poUsePipes];
316316 SegundoProceso.Options := [poUsePipes,poStderrToOutPut];
317317
318318 PrimerProceso.CommandLine := 'pwd';
319319 SegundoProceso.CommandLine := 'grep '+ DirectorySeparator+ ' -';
320320 // esto sería lo mismo que "pwd | grep / -"
321321
322322 PrimerProceso.Execute;
323323 SegundoProceso.Execute;
324324
325325 while PrimerProceso.Running or (PrimerProceso.Output.NumBytesAvailable > 0) do
326326 begin
327327 if PrimerProceso.Output.NumBytesAvailable > 0 then
328328 begin
329329 // asegurate de que no lees más datos que los asignados
330330 // en el buffer
331331 TamanyoLectura := PrimerProceso.Output.NumBytesAvailable;
332332 if TamanyoLectura > SizeOf(Buffer) then
333333 TamanyoLectura := SizeOf(Buffer);
334334 // ahora leemos la salida en el buffer
335335 ContadorLectura := PrimerProceso.Output.Read(Buffer[0], TamanyoLectura);
336336 // y escribimos el buffer en el segundo proceso
337337 SegundoProceso.Input.Write(Buffer[0], ContadorLectura);
338338
339339 // Si SegundoProceso escribe muchos datos en la salida a continuación,
340340 // debes leer los datos aquí para evitar un punto muerto
341341 // ver el ejemplo anterior "Lectura de muchos datos de salida"
342342 end;
343343 end;
344344 // Cerrar la entrad de SegundoProceso
345345 // y finaliza el procesamiento de datos
346346 SegundoProceso.CloseInput;
347347
348348 // y esperar a que finalice
349349 // ten cuidado con que comando se ejecuta porque no puede terminar cuando
350350 // la entrada está cerrada y la siguiente línea será un bucle sin fin
351351 while SegundoProceso.Running do
352352 Sleep(1);
353353 // ¡eso es todo! el resto del programa es sólo un ejemplo
354354 // es poco "útil"
355355
356356 // vamos a reutilizar el buffer para la salida del SegundoProceso
357357 // a la salida estándar de este programa
358358 WriteLn('Comienza la salida de Grep:');
359359 TamanyoLectura := SegundoProceso.Output.NumBytesAvailable;
360360 if TamanyoLectura > SizeOf(Buffer) then
361361 TamanyoLectura := SizeOf(Buffer);
362362 if TamanyoLectura > 0 then
363363 begin
364364 ContadorLectura := SegundoProceso.Output.Read(Buffer, TamanyoLectura);
365365 WriteLn(Copy(Buffer,0, ContadorLectura));
366366 end
367367 else
368368 WriteLn('grep NO encontró lo que se buscaba. ', SegundoProceso.ExitStatus);
369369 WriteLn('Termina la salida de Grep:');
370370
371371 // Liberamos los objetos TProcess
372372 PrimerProceso.Free;
373373 SegundoProceso.Free;
374374 end.
375375```
376376
377377   Eso es todo. Ahora podemos redirigir la salida de un programa a otro.
378378
379379#### Notas
380380
381381   Este ejemplo puede parecer exagerado, ya que es posible ejecutar "complicados" comandos shell con TProcess como:
382382
383383` Process.Commandline := 'sh -c "pwd | grep / -"';`
384384
385385   Pero el ejemplo es más multiplataforma, ya que no necesita ninguna modificación para ejecutarse en Windows o Linux, etc. *sh* puede o no existir en su plataforma y, en general sólo disponible en plataformas *nix. También tenemos más flexibilidad en nuestro ejemplo ya que se puede leer y escribir desde/a la entrada, salida y salida de error estándar de cada proceso individual, lo que podría ser muy ventajoso para tu proyecto.
386386
387387### Redirigir entrada y salida bajo *root*
388388
389389   Un problema común en Unix (macOS) y Linux surge cuando se desea ejecutar algún programa en la cuenta de *root* (o, en otra cuenta de usuario). Un ejemplo sería ejecutar el comando ping''''.
390390
391391   Si puedes usar sudo para esto, se puede adaptar el ejemplo siguiente, una adaptación de uno publicado por andyman en el foro ([1](http://lazarus.freepascal.org/index.php/topic,14479.0.html)). Este ejemplo ejecuta la orden `ls` en el `/root`, pero por supuesto puede ser adaptada.
392392
393393   "Una forma mejor'' de hacerlo es usar el paquete PolicyKit, que debería estar disponible en todos los Linux recientes. [ver el hilo del foro para más detalles.](http://lazarus.freepascal.org/index.php/topic,14479.0.html)
394394
395395   Gran parte de este código es similar al ejemplo anterior, pero también se muestra cómo redirigir los flujos stdout y stderr del proceso llamado a los flujos stdout y stderr de nuestro propio código.
396396
397397``` pascal
398398 program rootls;
399399
400400 { Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
401401 calling sudo on Linux/macOS, and supplying input on stdin}
402402 {$mode objfpc}{$H+}
403403
404404 uses
405405 Classes,
406406 Math, {for min}
407407 Process;
408408
409409 procedure RunsLsRoot;
410410 var
411411 Proc: TProcess;
412412 CharBuffer: array [0..511] of char;
413413 ReadCount: integer;
414414 ExitCode: integer;
415415 SudoPassword: string;
416416 begin
417417 WriteLn('Please enter the sudo password:');
418418 Readln(SudoPassword);
419419 ExitCode := -1; //Start out with failure, let's see later if it works
420420 Proc := TProcess.Create(nil); //Create a new process
421421 try
422422 Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
423423 Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
424424 // -S causes sudo to read the password from stdin.
425425 Proc.Execute; //start it. sudo will now probably ask for a password
426426
427427 // write the password to stdin of the sudo program:
428428 SudoPassword := SudoPassword + LineEnding;
429429 Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
430430 SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
431431 SudoPassword := ''; // and make the program a bit safer from snooping?!?
432432
433433 // main loop to read output from stdout and stderr of sudo
434434 while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
435435 (Proc.Stderr.NumBytesAvailable > 0) do
436436 begin
437437 // read stdout and write to our stdout
438438 while Proc.Output.NumBytesAvailable > 0 do
439439 begin
440440 ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
441441 Proc.Output.Read(CharBuffer, ReadCount);
442442 Write(StdOut, Copy(CharBuffer, 0, ReadCount));
443443 end;
444444 // read stderr and write to our stderr
445445 while Proc.Stderr.NumBytesAvailable > 0 do
446446 begin
447447 ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
448448 Proc.Stderr.Read(CharBuffer, ReadCount);
449449 Write(StdErr, Copy(CharBuffer, 0, ReadCount));
450450 end;
451451 end;
452452 ExitCode := Proc.ExitStatus;
453453 finally
454454 Proc.Free;
455455 Halt(ExitCode);
456456 end;
457457 end;
458458
459459 begin
460460 RunsLsRoot;
461461 end.
462462```
463463
464464   Otras cuestiones:
465465
466466   Sería, sin duda, aconsejable ver si realmente *sudo* pide una contraseña. Esto se puede comprobar consistetemente estableciendo la variable de entorno *SUDO_PROMPT* a algo que tener en cuenta mientras la lectura de la salida estándar de ***TProcess*** para evitar el problema de que el sistema es diferente para diferentes idiomas. Configurar una variable de entorno hace que los valores predeterminados (heredados de nuestro proceso) se borren, así que tenemos que copiar el entorno de nuestro programa, si es necesario.
467467
468468### Parámetros que contienen espacios (Reemplazar comillas del interprete)
469469
470470   En el interprete de Linux, es posible escribir los argumentos entrecomillados así:
471471
472472` gdb --batch --eval-command="info symbol 0x0000DDDD" miprograma`
473473
474474   Y GDB recibirá 3 argumentos (además del primer argumento que es la ruta completa al ejecutable):
475475
476476` #--batch`\
477477` #--eval-command=info symbol 0x0000DDDD`\
478478` #la ruta completa a miprograma`
479479
480480   **TProcess** además pasa los parámetros que contengan espacios, pero utiliza un estilo diferente de entrecomillado. En lugar de sólo entrecomillar parte del parámetro, entrecomilla todo él. Tal que así:
481481
482482` TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /midirectorio/yo/miprograma';`
483483
484484   Y también recuerda pasar sólo rutas completas.
485485
486486   Véase también la discusión al respecto: <http://bugs.freepascal.org/view.php?id=14446>
487487
488488## Alternativas
489489
490490   A veces, no es necesario llamar explícitamente a un programa externo para obtener la funcionalidad que necesitas. A continuación se presentan algunos ejemplos
491491
492492### Abrir una página web en el navegador web predeterminado
493493
494494   Algo así sería más fácil:
495495
496496``` pascal
497497 uses LCLIntf;
498498 ...
499499 OpenURL(URL);
500500```
501501
502502   Sin embargo, puedes utilizar algo como esto:
503503
504504``` pascal
505505 uses Process, LCLIntf;
506506
507507 procedure OpenWebPage(URL: string);
508508 // Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
509509 var
510510 Browser, Params: string;
511511 begin
512512 FindDefaultBrowser(Browser, Params);
513513 with TProcess.Create(nil) do
514514 try
515515 Executable := Browser;
516516 Params:=Format(Params, [URL]);
517517 Params:=copy(Params,2,length(Params)-2); // elimina "", la nueva versión de TProcess.Parameters los hace por sí mismo
518518 Parameters.Add(Params);
519519 Options := [poNoConsole];
520520 Execute;
521521 finally
522522 Free;
523523 end;
524524 end;
525525```
526526
527527## Ver también
528528
529529- [[TXMLPropStorage/es|TXMLPropStorage]]
530530- [[Webbrowser]]
531531
532532---
533533*From the Free Pascal wiki, page "Executing External Programs/es", as it stood on 2020-08-26 after a change by Iskraelectrica, out of the wiki's dump of 2024-05-26. 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).*
534534Category: Castellano
535535Category: Español

Back to history