Welcome to the new DelphiDabbler Code Library Documentation.

This is a new site that's currently running on alpha code. There are going to be bugs. If you discover any, please report them on the site's issues page (GitHub account required). Thanks.

Warning: Many URLs are going to change. Refer to the README file to discover which library project's documentation has been completed.

Console Application Runner Classes Example 6: Redirecting standard i/o using files

Applies to: ~>3.0

Console applications are often used with redirected input and output. It is quite common for a console application to process content read from standard input and to write the processed data to standard output. When we want to process files we must redirect the input file to standard input and redirect standard output to an output file.

Redirection from the command line is done using the < and > redirection operators (and similar). For example, to get MyApp.exe to read In.txt and write Out.txt we would use:

MyApp <In.txt >Out.txt

When redirecting programmatically we can’t use > and < on the command line, but must instead open handles to the files and pass those handles to Windows. Furthermore, on NT systems, the file handles must be inheritable.

TPJConsoleApp provides properties StdIn, StdOut and StdErr to implement redirection. Simply assign an inheritable handle to one or more of these properties to redirect standard input, standard output and standard error respectively.

To demonstrate this we need a console application that processes text from standard input and writes the processed text to standard output.

This example uses Echoer.exe to process the text. The source code for Echoer can be found in Appendix 2. If you have a suitable alternative application substitute it in the code below.

Note that Echoer expects to receive ANSI text on standard input and writes ANSI text to standard output. Any alternative application must do the same.

Start a new GUI application and drop a button and two memos on the main form. The text to be processed will be entered in Memo1. We will then write the memo content to an ANSI text file, process the file using Echoer.exe which will write an output file, then load that file into Memo2.

We will use the helper class TPJFileHandle to open the files and obtain their handles. The advantage of using this class is that it opens files with the required inheritable handles. It also closes the handles when the class is freed. You don’t have to use the class, but if you don’t use must open the required files yourself, ensuring they have inheritable handles. You must also close the handles when they are no longer needed. Some sample code is in Appendix 1.

TPJFileHandle is included in the I/O Utitlity Classes download in PJFileHandle.pas.

First make sure that the uses statement includes PJConsoleApp and PJFileHandle then create an OnClick event handler for the button as follows:

procedure TForm1.Button1Click(Sender: TObject);
const
  cInFile = 'Demo6-in.txt';
  cOutFile = 'Demo6-out.txt';
var
  App: TPJConsoleApp;
  InFile, OutFile: TPJFileHandle;
begin
  Memo1.Lines.SaveToFile(cInFile);
  InFile := nil;
  OutFile := nil;
  App := TPJConsoleApp.Create;
  try
    // Open input file and create output file with inheritable handles
    InFile := TPJFileHandle.Create(cInFile, fmOpenRead or fmShareDenyNone);
    OutFile := TPJFileHandle.Create(cOutFile, fmCreate or fmShareExclusive);
    // Redirect app's standard input and standard output
    App.StdIn := InFile.Handle;
    App.StdOut := OutFile.Handle;
    // Run the app without showing console
    App.Visible := False;
    if not App.Execute('Echoer ">>> "') then
      raise Exception.CreateFmt(
        'Error %X: %s', [App.ErrorCode, App.ErrorMessage]
      );
  finally
    OutFile.Free; // closes output file handle
    InFile.Free;  // closes input file handle
    App.Free;
  end;
  // Output file should now contain application output
  Memo2.Lines.LoadFromFile(cOutFile);
end;

We have not created an OnWork event handler for this demo. This is because we expect the text processing application to run very quickly and therefore will have a negligible effect on our application responsiveness.

Run the application, enter some text in Memo1 and click the button. The resulting processed file will be displayed in Memo2. The files Eg6-in.txt and Eg6-out.txt will have been created.

The sample code above works well with both Unicode and non-Unicode versions of Delphi. This is because the LoadFromFile and SaveToFile methods that the memo controls use for writing and reading the files create ANSI text files in both cases. In non-Unicode Delphis this is the only possible action and in Unicode Delphis it is the default action if no encoding is specified.