2010-06-02 14 views
6

Tengo que iniciar dos programas externos en mi programa y conectar el STDOUT del primero al STDIN del segundo programa. ¿Cómo se puede lograr esto en Delphi (RAD Studio 2009, si es importante)? Estoy operando en el entorno de Windows.Comience dos procesos y conéctelos con un conducto en Delphi

Como comandos comando mi situación sería algo como esto:

dumpdata.exe | encrypt.exe "mydata.dat" 

Respuesta

9

Una prueba rápida que parece funcionar (inspirado en gran medida por JCL):

child1: decir 'Hola, mundo!' 3x a la salida estándar

program child1; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

procedure Main; 
var 
    I: Integer; 
begin 
    for I := 0 to 2 do 
    Writeln('Hello, world!'); 
    Write(^Z); 
end; 

begin 
    try 
    Main; 
    except 
    on E: Exception do 
    begin 
     ExitCode := 1; 
     Writeln(ErrOutput, Format('[%s] %s', [E.ClassName, E.Message])); 
    end; 
    end; 
end. 

child2: echo lo que viene en la entrada estándar a OutputDebugString (pueden ser vistos por DebugView)

program child2; 

{$APPTYPE CONSOLE} 

uses 
    Windows, SysUtils, Classes; 

procedure Main; 
var 
    S: string; 
begin 
    while not Eof(Input) do 
    begin 
    Readln(S); 
    if S <> '' then 
     OutputDebugString(PChar(S)); 
    end; 
end; 

begin 
    try 
    Main; 
    except 
    on E: Exception do 
    begin 
     ExitCode := 1; 
     Writeln(ErrOutput, Format('[%s] %s', [E.ClassName, E.Message])); 
    end; 
    end; 
end. 

padres: lanzamiento child1 redirigido a CHILD2

program parent; 

{$APPTYPE CONSOLE} 

uses 
    Windows, Classes, SysUtils; 

procedure ExecutePiped(const CommandLine1, CommandLine2: string); 
var 
    StartupInfo1, StartupInfo2: TStartupInfo; 
    ProcessInfo1, ProcessInfo2: TProcessInformation; 
    SecurityAttr: TSecurityAttributes; 
    PipeRead, PipeWrite: THandle; 
begin 
    PipeWrite := 0; 
    PipeRead := 0; 
    try 
    SecurityAttr.nLength := SizeOf(SecurityAttr); 
    SecurityAttr.lpSecurityDescriptor := nil; 
    SecurityAttr.bInheritHandle := True; 
    Win32Check(CreatePipe(PipeRead, PipeWrite, @SecurityAttr, 0)); 

    FillChar(StartupInfo1, SizeOf(TStartupInfo), 0); 
    StartupInfo1.cb := SizeOf(TStartupInfo); 
    StartupInfo1.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; 
    StartupInfo1.wShowWindow := SW_HIDE; 
    StartupInfo1.hStdInput := GetStdHandle(STD_INPUT_HANDLE); 
    StartupInfo1.hStdOutput := PipeWrite; 
    StartupInfo1.hStdError := GetStdHandle(STD_ERROR_HANDLE); 

    FillChar(StartupInfo2, SizeOf(TStartupInfo), 0); 
    StartupInfo2.cb := SizeOf(TStartupInfo); 
    StartupInfo2.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; 
    StartupInfo2.wShowWindow := SW_HIDE; 
    StartupInfo2.hStdInput := PipeRead; 
    StartupInfo2.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE); 
    StartupInfo2.hStdError := GetStdHandle(STD_ERROR_HANDLE); 

    FillChar(ProcessInfo1, SizeOf(TProcessInformation), 0); 
    FillChar(ProcessInfo2, SizeOf(TProcessInformation), 0); 

    Win32Check(CreateProcess(nil, PChar(CommandLine2), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo2, 
     ProcessInfo2)); 

    Win32Check(CreateProcess(nil, PChar(CommandLine1), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo1, 
     ProcessInfo1)); 

    WaitForSingleObject(ProcessInfo2.hProcess, INFINITE); 
    finally 
    if PipeRead <> 0 then 
     CloseHandle(PipeRead); 
    if PipeWrite <> 0 then 
     CloseHandle(PipeWrite); 
    if ProcessInfo2.hThread <> 0 then 
     CloseHandle(ProcessInfo2.hThread); 
    if ProcessInfo2.hProcess <> 0 then 
     CloseHandle(ProcessInfo2.hProcess); 
    if ProcessInfo1.hThread <> 0 then 
     CloseHandle(ProcessInfo1.hThread); 
    if ProcessInfo1.hProcess <> 0 then 
     CloseHandle(ProcessInfo1.hProcess); 
    end; 
end; 

procedure Main; 
begin 
    ExecutePiped('child1.exe', 'child2.exe'); 
end; 

begin 
    try 
    Main; 
    except 
    on E: Exception do 
    begin 
     ExitCode := 1; 
     Writeln(Error, Format('[%s] %s', [E.ClassName, E.Message])); 
    end; 
    end; 
end. 
+0

Esto se ve exactamente lo que estaba buscando. Gracias. Por alguna razón, aunque ejecutar el programa principal me da "una infracción de acceso en el módulo kernel32.dll" en la primera línea CreateProcess. He construido todos los programas. Tal vez me falta algo ... – Steve

+0

No puedo ver nada en el código que pueda causar un A/V. Usé D2007 pero también debería funcionar en D2009. –

+3

La versión de carácter ancho de CreateProcess (que es lo que llamaría Delphi 2009) puede modificar la cadena de línea de comandos, por lo que no debe pasarle una cadena literal. Guárdelo en una variable de cadena y llame a UniqueString antes de convertirlo en PChar. –

0

Este enfoque debería funcionar. Antes de preocuparse por llamarlo desde Delphi, obtenga la línea de comando elaborada ejecutándose en una ventana de símbolo del sistema (ventana de DOS).
Luego solo llame a ese comando desde Delphi con WinExec o ShellExecute. Hay opciones para llamar y esperar, o simplemente "disparar y olvidar".

+0

El comando funciona bien en la línea de comandos. Si quiero esperar a que finalicen los procesos, ¿cómo podría lograrlo? Mi preocupación es que ¿cómo debería incluirse el comando de canalización en los parámetros de ShellExecute (Ex)? ¿Debe estar completamente en el parámetro lpFile o parcialmente en los lpParameters? – Steve

+1

AFAIK es el shell de comandos (que no es una ventana "DOS", es un subsistema win32/64 no gui) para manejar operadores de redireccionamiento, no la API de Windows. Si es así, ese comando solo se puede usar invocando cmd.exe y pasándole esa línea de comando. –

2

CreateProcess() le permite redirigir tanto stdin como stdout de la aplicación iniciada. Su aplicación puede leer desde la primera extensión de la aplicación y escribir en la segunda aplicación stdin.

+0

¿Hay alguna manera de dejar de lado al intermediario y simplemente dejar que los dos procesos se comuniquen directamente entre ellos? – Steve

2

Aquí es el código corregido para trabajar en Delphi XE. Las cadenas de línea de comandos deben ser variables y también definidas arriba de la función EjecutarPublicidad.

program Parent; 

    {$APPTYPE CONSOLE} 

    uses 
     Windows, SysUtils, Classes; 

    var cmd1, cmd2 :string; 

    function ExecutePiped(CommandLine1: string; CommandLine2: string):string; 
    var 
     StartupInfo1, StartupInfo2 : TStartupInfo; 
     ProcessInfo1, ProcessInfo2 : TProcessInformation; 
     SecurityAttr    : TSecurityAttributes; 
     PipeRead, PipeWrite  : THandle; 
     Handle      : Boolean; 
     WorkDir     : String; 
    begin 
     PipeWrite := 0; 
     PipeRead := 0; 
     try 
     SecurityAttr.nLength    := SizeOf(SecurityAttr); 
     SecurityAttr.bInheritHandle  := True; 
     SecurityAttr.lpSecurityDescriptor := nil; 

     CreatePipe(PipeRead, PipeWrite, @SecurityAttr, 0); 

     FillChar(StartupInfo1, SizeOf(TStartupInfo), 0); 
     StartupInfo1.cb   := SizeOf(TStartupInfo); 
     StartupInfo1.dwFlags  := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; 
     StartupInfo1.wShowWindow := SW_HIDE; 
     StartupInfo1.hStdInput := GetStdHandle(STD_INPUT_HANDLE); 
     StartupInfo1.hStdOutput := PipeWrite; 
     StartupInfo1.hStdError := GetStdHandle(STD_ERROR_HANDLE); 

     FillChar(StartupInfo2, SizeOf(TStartupInfo), 0); 
     StartupInfo2.cb   := SizeOf(TStartupInfo); 
     StartupInfo2.dwFlags  := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; 
     StartupInfo2.wShowWindow := SW_HIDE; 
     StartupInfo2.hStdInput := PipeRead; 
     StartupInfo2.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE); 
     StartupInfo2.hStdError := GetStdHandle(STD_ERROR_HANDLE); 

     FillChar(ProcessInfo1, SizeOf(TProcessInformation), 0); 
     FillChar(ProcessInfo2, SizeOf(TProcessInformation), 0); 

     WorkDir := ''; 

     Handle := CreateProcess(nil, PChar(CommandLine2), nil, nil, True, 0, nil, PChar(WorkDir), StartupInfo2, ProcessInfo2); 
     Handle := CreateProcess(nil, PChar(CommandLine1), nil, nil, True, 0, nil, PChar(WorkDir), StartupInfo1, ProcessInfo1); 

     WaitForSingleObject(ProcessInfo2.hProcess, INFINITE); 

     finally 

     if PipeRead    <> 0 then CloseHandle(PipeRead); 
     if PipeWrite    <> 0 then CloseHandle(PipeWrite); 

     if ProcessInfo2.hThread <> 0 then CloseHandle(ProcessInfo2.hThread); 
     if ProcessInfo2.hProcess <> 0 then CloseHandle(ProcessInfo2.hProcess); 

     if ProcessInfo1.hThread <> 0 then CloseHandle(ProcessInfo1.hThread); 
     if ProcessInfo1.hProcess <> 0 then CloseHandle(ProcessInfo1.hProcess); 

     end; 

    end; 

    procedure Main; 
    begin 
     cmd1 := '"child1.exe"'; 
     cmd2 := '"child2.exe"'; 
     ExecutePiped(cmd1, cmd2); 
    end; 

    begin 
     try 
     Main; 
     except 
     on E: Exception do 
     begin 
      ExitCode := 1; 
      Writeln(Error, Format('[%s] %s', [E.ClassName, E.Message])); 
     end; 
     end; 
    end. 

Para probar he modificado Child2.pas para escribir el texto recibido en un archivo.

program Child2; 

    {$APPTYPE CONSOLE} 

    uses 
    Windows, SysUtils, Classes; 

    procedure Main; 
    var S: string; 
     OutFile : TextFile; 
    begin 
     AssignFile(OutFile, 'test.txt'); 
     Rewrite(OutFile); 
     while not Eof(Input) do 
     begin 
     Readln(S); 
     Writeln(OutFile,S); 
     //if S <> '' then OutputDebugString(PChar(S)); 
     end; 
     CloseFile(OutFile); 
    end; 

    begin 
     try 
     Main; 
     except 
     on E: Exception do 
     begin 
      ExitCode := 1; 
      Writeln(ErrOutput, Format('[%s] %s', [E.ClassName, E.Message])); 
     end; 
     end; 
    end. 
Cuestiones relacionadas