在Delphi中启动两个进程并用管道连接它们

发布于 2024-09-04 09:54:08 字数 210 浏览 9 评论 0原文

我需要在我的程序中启动两个外部程序,并将第一个程序的 STDOUT 连接到第二个程序的 STDIN。如何在 Delphi 中实现这一目标(RAD Studio 2009,如果重要的话)?我是在Windows环境下操作的。

作为命令行命令,我的情况将如下所示:

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

I need to launch two external programs in my program and connect the STDOUT of the first one to the STDIN of the second program. How can you achieve this in Delphi (RAD Studio 2009, if it matters)? I'm operating in Windows environment.

As a commandline command my situation would look something like this:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

掩耳倾听 2024-09-11 09:54:08

一个似乎有效的快速测试(深受 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.

parent:启动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.

A quick test which seems to work (inspired heavily by JCL):

child1: say 'Hello, world!' 3x to standard output

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 whatever comes on standard input to OutputDebugString (can be viewed by 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.

parent: launch child1 redirected to 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.
堇色安年 2024-09-11 09:54:08

CreateProcess() 允许您重定向启动的应用程序的标准输入和标准输出。您的应用程序可以从第一个应用程序标准输出读取数据并写入第二个应用程序标准输入。

CreateProcess() allows you to redirect both stdin and stdout of application launched. Your application can read from the first app stdout and write to the second app stdin.

音盲 2024-09-11 09:54:08

以下是在 Delphi XE 中运行的更正代码。
命令行字符串必须是变量,并且也在 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.

Here is the corrected code to work in Delphi XE.
The CommandLine Strings must be variables and also defined above the ExecutePiped function.

    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.

To test I have modified Child2.pas to write the received text into a file.

    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.
南城追梦 2024-09-11 09:54:08

这种方法应该有效。在担心从 Delphi 调用它之前,请通过在命令提示符窗口(DOS 窗口)中运行来解决命令行问题。
然后只需使用 WinExec 或 ShellExecute 从 Delphi 调用该命令即可。可以选择拨打并等待,或者只是“即发即忘”。

That approach should work. Before worrying about calling it from Delphi, get the command line worked out by running in a command prompt window (DOS window).
Then just call that command from Delphi with WinExec or ShellExecute. There are options for calling and waiting, or just "fire and forget".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文