如何确定StdOut和StdError的正确顺序/缓冲区 [英] How to determine the proper order/buffer for StdOut and StdError

查看:437
本文介绍了如何确定StdOut和StdError的正确顺序/缓冲区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个外部控制台应用程序我想捕获它的完成输出(StdOut和StdError)实时到备忘录(就像我双击它)。

I have an external console application I would like to capture it's complete output (StdOut and StdError) in real time to a memo (just as if I would double click it).

有关应用程序的屏幕截图信息:
Delphi控制台

Infos about the application with screenshots: Delphi Console pipes switched?

我已经写了一个单位来读取管道:

I've written an unit to read the pipes:

unit uConsoleOutput;
interface

uses  Classes,
      StdCtrls,
      SysUtils,
      Messages,
      Windows;

  type
  ConsoleThread = class(TThread)
  private
    OutputString : String;
    procedure SetOutput;
  protected
    procedure Execute; override;
  public
    App           : WideString;
    Memo          : TMemo;
    Directory     : WideString;
  end;

  type
    PConsoleData = ^ConsoleData;
    ConsoleData = record
    OutputMemo          : TMemo;
    OutputApp           : WideString;
    OutputDirectory     : WideString;
    OutputThreadHandle  : ConsoleThread;
  end;

function StartConsoleOutput (App : WideString; Directory : WideString; Memo : TMemo) : PConsoleData;
procedure StopConsoleOutput  (Data : PConsoleData);

implementation

procedure ConsoleThread.SetOutput;
begin
  Memo.Lines.BeginUpdate;
  Memo.Text := Memo.Text + OutputString;
  Memo.Lines.EndUpdate;
end;

procedure ConsoleThread.Execute;
const
  ReadBuffer = 50;
var
  Security    : TSecurityAttributes;
  InputPipeRead,
  InputPipeWrite,
  OutputPipeRead,
  OutputPipeWrite,
  ErrorPipeRead,
  ErrorPipeWrite : THandle;
  start       : TStartUpInfo;
  ProcessInfo : TProcessInformation;
  Buffer      : Pchar;
  BytesRead   : DWord;
  Apprunning  : DWord;
begin
  Security.nlength := SizeOf(TSecurityAttributes) ;
  Security.lpsecuritydescriptor := nil;
  Security.binherithandle := true;
  if Createpipe (InputPipeRead, InputPipeWrite, @Security, 0) then begin
    if CreatePipe(OutputPipeRead, OutputPipeWrite, @Security, 0) then begin
      if CreatePipe(ErrorPipeRead, ErrorPipeWrite, @Security, 0) then begin
        Buffer := AllocMem(ReadBuffer + 1) ;
        FillChar(Start,Sizeof(Start),#0) ;
        start.cb := SizeOf(start) ;
        start.hStdOutput  := OutputPipeWrite;
        start.hStdError   := ErrorPipeWrite;
        start.hStdInput   := InputPipeRead;
        start.dwFlags     := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
        start.wShowWindow := SW_HIDE;
        if CreateProcessW(nil,pwidechar(APP),@Security,@Security,true,NORMAL_PRIORITY_CLASS,nil,pwidechar(Directory),start,ProcessInfo) then begin
          while not(terminated) do begin

          // ====> Stuck here.
            // ReadErrorPipe
            BytesRead := 0;
            if Terminated then break;
            ReadFile(ErrorPipeRead,Buffer[0], ReadBuffer,BytesRead,nil);
            if Terminated then break;
            Buffer[BytesRead]:= #0;
            if Terminated then break;
            //OemToAnsi(Buffer,Buffer);
            if Terminated then break;
            OutputString := Buffer;
            if Terminated then break;
            Synchronize(SetOutput);

            // ReadStdOut
            BytesRead := 0;
            if Terminated then break;
            ReadFile(OutputPipeRead,Buffer[0], ReadBuffer,BytesRead,nil);
            if Terminated then break;
            Buffer[BytesRead]:= #0;
            if Terminated then break;
            //OemToAnsi(Buffer,Buffer);
            if Terminated then break;
            OutputString := Buffer;
            if Terminated then break;
            Synchronize(SetOutput);

            end;
          FreeMem(Buffer);
          CloseHandle(ProcessInfo.hProcess);
          CloseHandle(ProcessInfo.hThread);
          CloseHandle(InputPipeRead);
          CloseHandle(InputPipeWrite);
          CloseHandle(OutputPipeRead);
          CloseHandle(OutputPipeWrite);
          CloseHandle(ErrorPipeRead);
          CloseHandle(ErrorPipeWrite);
        end;
      end;
    end;
  end;
end;

function StartConsoleOutput (App : WideString; Directory : WideString; Memo : TMemo) : PConsoleData;
begin
  result                          := VirtualAlloc(NIL, SizeOf(ConsoleData), MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  Memo.DoubleBuffered             := TRUE;
  with PConsoleData(result)^ do begin
    OutputMemo                          := Memo;
    OutputApp                           := App;
    OutputDirectory                     := Directory;
    OutputThreadHandle                  := ConsoleThread.Create(TRUE);
    OutputThreadHandle.FreeOnTerminate  := TRUE;
    OutputThreadHandle.Memo             := Memo;
    OutputThreadHandle.App              := App;
    OutputThreadHandle.Directory        := Directory;
    OutputThreadHandle.Resume;
  end;
end;

procedure StopConsoleOutput  (Data : PConsoleData);
begin
  with PConsoleData(Data)^ do begin
    OutputThreadHandle.Terminate;
    while not(OutputThreadHandle.Terminated) do sleep (100);
  end;
  VirtualFree (Data,0, MEM_RELEASE);
end;

end.

我启动这样的应用程序:

I start applications like this:

StartConsoleOutput ('C:\myexternalapp.exe', 'C:\', Memo1);

我想先输出stderror,然后输出stdoutput(或至少以1:1控制台输出什么)
问题是顺序和适当的缓冲区。

I would like to ouput the stderror first and then the stdoutput (or at least in an order that reads 1:1 what the console outputs) The problem is the order and the proper buffersize.

如何以正确的顺序读取这两个管道,并使用哪个缓冲区大小来完成1:1输出?

How do I read these 2 pipes in the right order and with which buffer size to accomplish an 1:1 output?

推荐答案

使用 PeekNamedPipe() 确定管道何时有可用于读取的数据以及可读取的字节数。尽管它的名称,它与命名管道和匿名管道。

Use PeekNamedPipe() to determine when a pipe has data available for reading and how many bytes can be read. Despite its name, it works with both named pipes and anonymous pipes.

这篇关于如何确定StdOut和StdError的正确顺序/缓冲区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆