2010-06-02 7 views
6

Мне нужно запустить две внешние программы в моей программе и подключить STDOUT первого к STDIN второй программы. Как вы можете достичь этого в Delphi (RAD Studio 2009, если это имеет значение)? Я работаю в среде Windows.Запустите два процесса и соедините их с трубой в Delphi

В командной строке команду моя ситуация будет выглядеть примерно так:

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

ответ

9

Быстрый тест, который, кажется, работает (вдохновленный в большой степени JCL):

child1: сказать 'Привет, мир!' 3x на стандартный вывод

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: эхо все, что приходит на стандартный ввод в OutputDebugString (может быть просмотрен 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. 

Родитель: запуск child1 перенаправлены 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

Это выглядит именно так, что я искал. Спасибо. По какой-то причине, хотя запуск родительской программы дает мне «нарушение доступа в модуле kernel32.dll» на первой строке CreateProcess. Я создал все программы. Может быть, мне что-то не хватает ... – Steve

+0

Я ничего не вижу в коде, который может вызвать A/V. Я использовал D2007, но он также должен работать в D2009. –

+3

Широкосимвольная версия CreateProcess (которая будет вызывать Delphi 2009) может изменить строку командной строки, поэтому вы не должны передавать строковый литерал. Сохраните его в строковой переменной и вызовите UniqueString перед тем, как набросить его на PChar. –

0

Такой подход должен работать. Прежде чем беспокоиться о вызове его из Delphi, получите командную строку, проработав в окне командной строки (окно DOS).
Затем просто вызовите эту команду из Delphi с помощью WinExec или ShellExecute. Есть варианты для вызова и ожидания, или просто «огонь и забыть».

+0

Команда хорошо работает в командной строке. Если я хочу дождаться завершения процессов, как я могу это достичь? Мое беспокойство заключается в том, как следует включить команду pipeed в параметры ShellExecute (Ex)? Должен ли он полностью находиться в параметре lpFile или частично в lpParameters? – Steve

+1

AFAIK - это командная оболочка (которая не является окном DOS, это не-gui win32/64 subsytem) для обработки операторов перенаправления, а не для самих API Windows. Если это так, что команда может быть использована только для вызова cmd.exe и передачи этой командной строки. – 2010-06-02 12:48:48

2

CreateProcess() позволяет перенаправить оба stdin и stdout приложения. Ваше приложение может читать из первого приложения stdout и записывать во второе приложение stdin.

+0

Есть ли способ уйти от среднего человека и позволить двум процессам общаться напрямую друг с другом? – Steve

2

Здесь это исправленный код для работы в Delphi XE. Строки CommandLine должны быть переменными, а также определенными над функцией ExecutePiped.

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. 

Для проверки я изменил Child2.pas, чтобы записать полученный текст в файл.

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. 
Смежные вопросы