如何确定StdOut和StdError的正确顺序/缓冲区 [英] How to determine the proper order/buffer for StdOut and 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屋!