Mega Code Archive

 
Categories / Delphi / Examples
 

Use Pipes (or receive input and output from a console)

Title: Use Pipes (or receive input and output from a console) 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 WriteFile(OutputPipe, Instring[1], Length(Instring), byteswritten, nil); Read example later. Full examples are in the file below, along with other places. Do I have something in the pipe line? The question of reading a pipe has this question first and foremost, since there is always the possibility of having nothing to read. This is solved by the PeekNamedPipe function. It checks the pipe and returns information related to its state. Attempting to read an empty pipe will cause a program crash, so using this function is a necessity. CODE PeekNamedPipe(InputPipe, nil, PipeSize, @BytesRead, @PipeSize, @BytesRem); if BytesRead 0 then ReadFile(InputPipe, TextBuffer, pipesize, bytesread, nil); While you can control how fast you can send input to a program, you can't control how fast a program sends its output to you (for example, issuing dir /s sends a lot of text very fast). So define a big enough buffer to receive any output you get as quickly as possible (so you don't lose any), since this is a realtime process and pipes are limited in their buffer size. I used 32K and it seemed to work well in all my testing. Closing a Pipe As with any resource, you have to close it when you are done with it. With pipes you have to close both ends. CODE 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. Sample file here: Sample here