2010-06-02 3 views
6

프로그램에서 두 개의 외부 프로그램을 시작하고 첫 번째 프로그램의 STDOUT을 두 번째 프로그램의 STDIN에 연결해야합니다. Delphi (RAD Studio 2009, 중요하다면)에서 이것을 어떻게 얻을 수 있습니까? Windows 환경에서 작업하고 있습니다. 명령 행으로 두 프로세스를 시작하여 델파이의 파이프와 연결하십시오.

내 상황은 다음과 같이 보일 것입니다 명령 :

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

답변

9

작동하는 것 같다 빠른 테스트 (JCL에 의해 크게 영감) :

자식 1을 : 대답 '안녕하세요, 세계!' 표준 출력으로 3 배

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. 

자식 2 : 어떤이 (DebugView 볼 수 있습니다) OutputDebugString로에 표준 입력에 제공 에코

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. 

부모 : 발사 자식 1은 여기

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 창)에서 명령 행을 실행하십시오.
그런 다음 WinExec 또는 ShellExecute를 사용하여이 명령을 Delphi에서 호출하십시오. 전화 걸기와 대기, 또는 단지 "불과 잊기"옵션이 있습니다.

+0

명령은 명령 행에서 잘 작동합니다. 프로세스가 끝나기를 기다리려면 어떻게해야합니까? 내 걱정은 파이프 된 명령을 ShellExecute (Ex) 매개 변수에 어떻게 포함시켜야합니까? 그것은 완전히 lpFile 매개 변수에 있거나 부분적으로 lpParameters에 있어야합니까? – Steve

+1

AFAIK는 Windows API 자체가 아닌 리디렉션 연산자를 처리하는 명령 쉘입니다 ("DOS"창이 아니며 win32/64가 아닌 하위 시스템 임). 그렇다면 명령은 cmd.exe를 호출하고 명령 행을 전달하는 데에만 사용될 수 있습니다. –

2

CreateProcess()를 사용하면 시작한 응용 프로그램의 stdin과 stdout을 모두 리디렉션 할 수 있습니다. 응용 프로그램은 첫 번째 응용 프로그램 stdout에서 읽고 두 번째 응용 프로그램 stdin에 쓸 수 있습니다.

+0

중간 사람을 제외하고 두 프로세스가 서로 직접 통신하도록 할 수있는 방법이 있습니까? – Steve

2

을 자식 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.