Sign in Join
Wiki copytemporary

Executing External Programs/es: what changed

Revision 1 to 2. 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-<figure>
258257<img src="TProcess_Options.PNG" title="TProcess_Options.PNG" width="300" />
259-<figcaption>TProcess_Options.PNG</figcaption>
260-</figure>
261258
262259A falta de explicar que hace cada opción.
263260
264261[Referencia TProcess](http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html).
265262
266263### Ejemplo de *conversación* con el proceso aspell
267264
268265   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:
269266
270267- [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.
271268
272269- [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.
273270
274271   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.
275272
276273### Reemplazar operadores de *Shell* como "\|", "\<" y "\>"
277274
278275   A veces queremos ejecutar una orden compleja que envía (entuba) sus datos a otra orden o a un archivo. Algo como
279276
280277` ShellExecute('primeraorden.exe | segundaorden.exe');`
281278
282279o
283280
284281` ShellExecute('dir > salida.txt');`
285282
286283    Esto no funciona con TProcess. p.e.:
287284
288285` // esto no funciona`\
289286` Process.CommandLine := 'primeraorden.exe | segundaorden.exe'; `\
290287` Process.Execute;`
291288
292289#### ¿Por qué utilizar operadores especiales para redirigir la salida no funciona?
293290
294291   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]].
295292
296293### Cómo redirigir la salida con TProcess
297294
298295   Podemos redirigir la salidad de un comando a otro comando utilizando una instancia de TProcess para cada commando.
299296
300297   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]]
301298
302299``` pascal
303300 program Proyecto1;
304301
305302 uses
306303 Classes, sysutils, process;
307304
308305 var
309306 PrimerProceso,
310307 SegundoProceso: TProcess;
311308 Buffer: array[0..127] of char;
312309 ContadorLectura: Integer;
313310 TamanyoLectura: Integer;
314311 begin
315312 PrimerProceso := TProcess.Create(nil);
316313 SegundoProceso := TProcess.Create(nil);
317314
318315 PrimerProceso.Options := [poUsePipes];
319316 SegundoProceso.Options := [poUsePipes,poStderrToOutPut];
320317
321318 PrimerProceso.CommandLine := 'pwd';
322319 SegundoProceso.CommandLine := 'grep '+ DirectorySeparator+ ' -';
323320 // esto sería lo mismo que "pwd | grep / -"
324321
325322 PrimerProceso.Execute;
326323 SegundoProceso.Execute;
327324
328325 while PrimerProceso.Running or (PrimerProceso.Output.NumBytesAvailable > 0) do
329326 begin
330327 if PrimerProceso.Output.NumBytesAvailable > 0 then
331328 begin
332329 // asegurate de que no lees más datos que los asignados
333330 // en el buffer
334331 TamanyoLectura := PrimerProceso.Output.NumBytesAvailable;
335332 if TamanyoLectura > SizeOf(Buffer) then
336333 TamanyoLectura := SizeOf(Buffer);
337334 // ahora leemos la salida en el buffer
338335 ContadorLectura := PrimerProceso.Output.Read(Buffer[0], TamanyoLectura);
339336 // y escribimos el buffer en el segundo proceso
340337 SegundoProceso.Input.Write(Buffer[0], ContadorLectura);
341338
342339 // Si SegundoProceso escribe muchos datos en la salida a continuación,
343340 // debes leer los datos aquí para evitar un punto muerto
344341 // ver el ejemplo anterior "Lectura de muchos datos de salida"
345342 end;
346343 end;
347344 // Cerrar la entrad de SegundoProceso
348345 // y finaliza el procesamiento de datos
349346 SegundoProceso.CloseInput;
350347
351348 // y esperar a que finalice
352349 // ten cuidado con que comando se ejecuta porque no puede terminar cuando
353350 // la entrada está cerrada y la siguiente línea será un bucle sin fin
354351 while SegundoProceso.Running do
355352 Sleep(1);
356353 // ¡eso es todo! el resto del programa es sólo un ejemplo
357354 // es poco "útil"
358355
359356 // vamos a reutilizar el buffer para la salida del SegundoProceso
360357 // a la salida estándar de este programa
361358 WriteLn('Comienza la salida de Grep:');
362359 TamanyoLectura := SegundoProceso.Output.NumBytesAvailable;
363360 if TamanyoLectura > SizeOf(Buffer) then
364361 TamanyoLectura := SizeOf(Buffer);
365362 if TamanyoLectura > 0 then
366363 begin
367364 ContadorLectura := SegundoProceso.Output.Read(Buffer, TamanyoLectura);
368365 WriteLn(Copy(Buffer,0, ContadorLectura));
369366 end
370367 else
371368 WriteLn('grep NO encontró lo que se buscaba. ', SegundoProceso.ExitStatus);
372369 WriteLn('Termina la salida de Grep:');
373370
374371 // Liberamos los objetos TProcess
375372 PrimerProceso.Free;
376373 SegundoProceso.Free;
377374 end.
378375```
379376
380377   Eso es todo. Ahora podemos redirigir la salida de un programa a otro.
381378
382379#### Notas
383380
384381   Este ejemplo puede parecer exagerado, ya que es posible ejecutar "complicados" comandos shell con TProcess como:
385382
386383` Process.Commandline := 'sh -c "pwd | grep / -"';`
387384
388385   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.
389386
390387### Redirigir entrada y salida bajo *root*
391388
392389   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''''.
393390
394391   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.
395392
396393   "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)
397394
398395   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.
399396
400397``` pascal
401398 program rootls;
402399
403400 { Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
404401 calling sudo on Linux/macOS, and supplying input on stdin}
405402 {$mode objfpc}{$H+}
406403
407404 uses
408405 Classes,
409406 Math, {for min}
410407 Process;
411408
412409 procedure RunsLsRoot;
413410 var
414411 Proc: TProcess;
415412 CharBuffer: array [0..511] of char;
416413 ReadCount: integer;
417414 ExitCode: integer;
418415 SudoPassword: string;
419416 begin
420417 WriteLn('Please enter the sudo password:');
421418 Readln(SudoPassword);
422419 ExitCode := -1; //Start out with failure, let's see later if it works
423420 Proc := TProcess.Create(nil); //Create a new process
424421 try
425422 Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
426423 Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
427424 // -S causes sudo to read the password from stdin.
428425 Proc.Execute; //start it. sudo will now probably ask for a password
429426
430427 // write the password to stdin of the sudo program:
431428 SudoPassword := SudoPassword + LineEnding;
432429 Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
433430 SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
434431 SudoPassword := ''; // and make the program a bit safer from snooping?!?
435432
436433 // main loop to read output from stdout and stderr of sudo
437434 while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
438435 (Proc.Stderr.NumBytesAvailable > 0) do
439436 begin
440437 // read stdout and write to our stdout
441438 while Proc.Output.NumBytesAvailable > 0 do
442439 begin
443440 ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
444441 Proc.Output.Read(CharBuffer, ReadCount);
445442 Write(StdOut, Copy(CharBuffer, 0, ReadCount));
446443 end;
447444 // read stderr and write to our stderr
448445 while Proc.Stderr.NumBytesAvailable > 0 do
449446 begin
450447 ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
451448 Proc.Stderr.Read(CharBuffer, ReadCount);
452449 Write(StdErr, Copy(CharBuffer, 0, ReadCount));
453450 end;
454451 end;
455452 ExitCode := Proc.ExitStatus;
456453 finally
457454 Proc.Free;
458455 Halt(ExitCode);
459456 end;
460457 end;
461458
462459 begin
463460 RunsLsRoot;
464461 end.
465462```
466463
467464   Otras cuestiones:
468465
469466   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.
470467
471468### Parámetros que contienen espacios (Reemplazar comillas del interprete)
472469
473470   En el interprete de Linux, es posible escribir los argumentos entrecomillados así:
474471
475472` gdb --batch --eval-command="info symbol 0x0000DDDD" miprograma`
476473
477474   Y GDB recibirá 3 argumentos (además del primer argumento que es la ruta completa al ejecutable):
478475
479476` #--batch`\
480477` #--eval-command=info symbol 0x0000DDDD`\
481478` #la ruta completa a miprograma`
482479
483480   **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í:
484481
485482` TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /midirectorio/yo/miprograma';`
486483
487484   Y también recuerda pasar sólo rutas completas.
488485
489486   Véase también la discusión al respecto: <http://bugs.freepascal.org/view.php?id=14446>
490487
491488## Alternativas
492489
493490   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
494491
495492### Abrir una página web en el navegador web predeterminado
496493
497494   Algo así sería más fácil:
498495
499496``` pascal
500497 uses LCLIntf;
501498 ...
502499 OpenURL(URL);
503500```
504501
505502   Sin embargo, puedes utilizar algo como esto:
506503
507504``` pascal
508505 uses Process, LCLIntf;
509506
510507 procedure OpenWebPage(URL: string);
511508 // Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
512509 var
513510 Browser, Params: string;
514511 begin
515512 FindDefaultBrowser(Browser, Params);
516513 with TProcess.Create(nil) do
517514 try
518515 Executable := Browser;
519516 Params:=Format(Params, [URL]);
520517 Params:=copy(Params,2,length(Params)-2); // elimina "", la nueva versión de TProcess.Parameters los hace por sí mismo
521518 Parameters.Add(Params);
522519 Options := [poNoConsole];
523520 Execute;
524521 finally
525522 Free;
526523 end;
527524 end;
528525```
529526
530527## Ver también
531528
532529- [[TXMLPropStorage/es|TXMLPropStorage]]
533530- [[Webbrowser]]
534531
535532---
536533*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).*
537534Category: Castellano
538535Category: Español

Back to history