Professional Documents
Culture Documents
Delpgi - Use Pipes (Or Receive Input and Output From A Console) - Embarcadero - Delphi FAQ - Tek-Tips
Delpgi - Use Pipes (Or Receive Input and Output From A Console) - Embarcadero - Delphi FAQ - Tek-Tips
http://www.tek-tips.com/faqs.cfm?fid=7402
Smart questions
Smart answers
Smart people
Join
Directory
Search
Tell A Friend
Whitepapers
Jobs
Home > Forums > Programmers > Development Tools > Embarcadero: Delphi > FAQs
How To
Use Pipes (or receive input and output from a console)
faq102-7402
Posted: 13 Nov 10 (Edited 21 Jun 11)
The question that is usually asked is: How do I run a command-line process, capture the output to a form, and provide input from a form?
Pipes are an OS facility provided to share memory between applications. They are ideal in most cases for directly receiving the output and providing input to a
command-line program. But they can be applied to any case where an inter-process communication can be desired.
Microsoft reference on Pipes
Complete code will not be posted here, but a downloadable sample will be provided.
Pipes are handled much like files are. You open them, write/read from them and close them. However, they are different in that each pipe has both an input
and an output. Both must be assigned and addressed. The analogy of a real pipe can be useful to understand this.
Create a Pipe
To do that you use the CreatePipe. The call looks like this:
CODE
var
Security : TSecurityAttributes;
With Security do
begin
nlength := SizeOf(TSecurityAttributes) ;
binherithandle := true;
lpsecuritydescriptor := nil;
end;
CreatePipe(InputPipeRead, InputPipeWrite, @Security, 0);
The security attributes block usually must be present for this to work, and seems to generally work well as listed.
InputPipeRead and InputPipeWrite are the two ends of the pipe and these handles are used to address the pipe.
Read and Write from a Pipe
The standard processes used to write or read a file are also used to read from and write to a pipe. These are ReadFile and WriteFile. These work much like
blockread or blockwrite.
CODE
1 of 6
8/20/2014 5:20 PM
Use Pipes (or receive input and output from a console) - Embarcadero: D...
http://www.tek-tips.com/faqs.cfm?fid=7402
CloseHandle(InputPipeRead);
CloseHandle(InputPipeWrite);
Hooking up the pipeline
Now that we have the basics of pipes down, the question now comes of how one is hooked up to a command-line process in order to route the input and
output through my form. In a CreateProcess call, you do this by specifying one side of the pipe for each of the standard console handles.
CODE
start.hStdInput := InputPipeRead;
start.hStdOutput := OutputPipeWrite;
start.hStdError := ErrorPipeWrite;
Now this part gets conceptually confusing. From your program's perspective you WRITE input and READ output - I used these handle names in this order as
a mnemonic to aid the concept.
From the perspective of the command-line app, StdInput is what is read from, StdOutput is what is written to. StdError is written when any error messages
occur (for example, "'xxxx' is not recognized as an internal or external command,
operable program or batch file.") - you might not have these in directly calling a command-line program, but it is always safe to define it.
Real-Time Process
Since piping from a command-line program is a real-time process, you will need to handle any significant inputs or outputs within a thread process. An
illustration is in the sample using TThread, but any method will work.
Full Sample Source Is Below
CODE
unit mcunit;
{ written by Glenn9999 @ tek-tips.com.
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
monitor = class(TThread) // pipe monitoring thread for console output
private
TextString: String;
procedure UpdateCaption;
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
CommandText: TMemo;
CommandRun: TComboBox;
Button2: TButton;
SaveDialog1: TSaveDialog;
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
2 of 6
8/20/2014 5:20 PM
Use Pipes (or receive input and output from a console) - Embarcadero: D...
http://www.tek-tips.com/faqs.cfm?fid=7402
cmdcount: integer;
end;
var
Form1: TForm1;
InputPipeRead, InputPipeWrite: THandle;
OutputPipeRead, OutputPipeWrite: THandle;
ErrorPipeRead, ErrorPipeWrite: THandle;
ProcessInfo : TProcessInformation;
myThread: monitor;
implementation
{$R *.DFM}
procedure WritePipeOut(OutputPipe: THandle; InString: string);
// writes Instring to the pipe handle described by OutputPipe
var
byteswritten: DWord;
begin
// most console programs require CR/LF after their input.
InString := InString + #13#10;
WriteFile(OutputPipe, Instring[1], Length(Instring), byteswritten, nil);
end;
function ReadPipeInput(InputPipe: THandle; var BytesRem: Integer): String;
{
reads console output from InputPipe. Returns the input in function
result. Returns bytes of remaining information to BytesRem
}
var
TextBuffer: array[1..32767] of char;
TextString: String;
BytesRead: Integer;
PipeSize: Integer;
begin
Result := '';
PipeSize := Sizeof(TextBuffer);
// check if there is something to read in pipe
PeekNamedPipe(InputPipe, nil, PipeSize, @BytesRead, @PipeSize, @BytesRem);
if bytesread > 0 then
begin
ReadFile(InputPipe, TextBuffer, pipesize, bytesread, nil);
// a requirement for Windows OS system components
OemToChar(@TextBuffer, @TextBuffer);
TextString := String(TextBuffer);
SetLength(TextString, BytesRead);
Result := TextString;
end;
end;
procedure monitor.Execute;
{ monitor thread execution for console output. This must be threaded.
checks the error and output pipes for information every 40 ms, pulls the
data in and updates the memo on the form with the output }
var
BytesRem: DWord;
begin
while not Terminated do
begin
// read regular output stream and put on screen.
TextString := ReadPipeInput(OutputPipeRead, BytesRem);
if TextString <> '' then
Synchronize(UpdateCaption);
// now read error stream and put that on screen.
3 of 6
8/20/2014 5:20 PM
Use Pipes (or receive input and output from a console) - Embarcadero: D...
http://www.tek-tips.com/faqs.cfm?fid=7402
4 of 6
8/20/2014 5:20 PM
Use Pipes (or receive input and output from a console) - Embarcadero: D...
http://www.tek-tips.com/faqs.cfm?fid=7402
Application.Terminate
else
dec(cmdcount);
end
else
WritePipeOut(InputPipeWrite, CommandRun.Text);
CommandRun.Items.Add(CommandRun.Text);
CommandRun.Text := '';
CommandRun.SetFocus;
end;
procedure TForm1.FormCreate(Sender: TObject);
{ upon form creation, this calls the command-interpreter, sets up the three
pipes to catch input and output, and starts a thread to monitor and show
the output of the command-interpreter }
var
DosApp: String;
DosSize: Integer;
Security : TSecurityAttributes;
start : TStartUpInfo;
begin
CommandText.Clear;
// get COMSPEC variable, this is the path of the command-interpreter
SetLength(Dosapp, 255);
DosSize := GetEnvironmentVariable('COMSPEC', @DosApp[1], 255);
SetLength(Dosapp, DosSize);
// create pipes
With Security do
begin
nlength := SizeOf(TSecurityAttributes) ;
binherithandle := true;
lpsecuritydescriptor := nil;
end;
CreatePipe(InputPipeRead, InputPipeWrite, @Security, 0);
CreatePipe(OutputPipeRead, OutputPipeWrite, @Security, 0);
CreatePipe(ErrorPipeRead, ErrorPipeWrite, @Security, 0);
// start command-interpreter
FillChar(Start,Sizeof(Start),#0) ;
start.cb := SizeOf(start) ;
start.hStdInput := InputPipeRead;
start.hStdOutput := OutputPipeWrite;
start.hStdError := ErrorPipeWrite;
start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
start.wShowWindow := SW_HIDE;
if CreateProcess(nil, PChar(DosApp), @Security, @Security, true,
CREATE_NEW_CONSOLE or SYNCHRONIZE,
nil, nil, start, ProcessInfo) then
begin
MyThread := monitor.Create(false); // start monitor thread
MyThread.Priority := tpHigher;
end;
Button2.Enabled := true;
cmdcount := 1;
end;
end.
Back to Embarcadero: Delphi FAQ Index
Back to Embarcadero: Delphi Forum
5 of 6
8/20/2014 5:20 PM
Use Pipes (or receive input and output from a console) - Embarcadero: D...
http://www.tek-tips.com/faqs.cfm?fid=7402
6 of 6
8/20/2014 5:20 PM