6

How do I write to stdout from Delphi console application?

Here's what I have tried. I have rigged this simple test app according to infos I could find, to read a file from disk and output it to console stdout:

program ConsoleOut;
{$APPTYPE CONSOLE}
uses
  Classes, Windows, SysUtils;

var
  S: TMemoryStream;
  OutputStream: THandleStream;
  ss: string;
  Buffer: PByte;
  i: Integer;
begin
  S := TMemoryStream.Create;
  S.LoadFromFile('1.jpg');
  S.Seek(0, soFromBeginning);

  //Am I right that from now on writing to OutputStream will write to stdout?
  OutputStream := THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE));

  GetMem(Buffer, S.Size);
    S.ReadBuffer(Buffer^, S.Size);
    i := OutputStream.Write(Buffer^, S.Size); //i = 0 here for some reason
  FreeMem(Buffer, S.Size);

  Writeln(i, ' byte written to output');
  Readln(ss); //Don't exit app to read previous line
  S.Free;
end.

But for some reason it fails. Could you please direct me into proper way of writing to stdout?

Kromster
  • 7,181
  • 7
  • 63
  • 111
  • curious WHY youd want to write binary noise to the console? You mention something about text to speech. Why not named pipes? – Warren P Mar 22 '13 at 01:14
  • 1
    What advantages do named pipes have against stdout? – Kromster Mar 22 '13 at 05:17
  • Being designed for serious data transfer instead of user interfaces,on Windows. Unix standard output is routinely used as an anonymous data pipe. Dos and Windows do not routinely use this. As you've learned. – Warren P Mar 22 '13 at 11:35

1 Answers1

7

Your approach is sound. However, if stdout is attached to a console then your code fails. A call to GetLastError following the stream write reveals the error code ERROR_NOT_ENOUGH_MEMORY:

Not enough storage is available to process this command.

If you redirect stdout to a file then your code will work fine. And surely you don't really want to spew binary data to the console. That's just going to put unreadable content on the console and make the computer beep annoyingly!

If you must output to the console then you'll need to find out how big the console device buffer is and write to it in appropriately sized chunks. I must confess that I'm not sure how you go about doing that. You could use trial and error, but that doesn't appeal to me. Presumably there is a way to query the console to find out the information.

Looking at the documentation for WriteConsole, it seems that 64K is the upper limit. And indeed if I write spaces to your handle stream then I can write nearly 64K in one go. However, if I write raw JPEG binary data, then it gives out earlier. So I think that's part of the problem too – don't dump a JPEG onto the console.

One other comment. Since you read the contents of the file into a memory stream, there's no need to allocate an intermediate buffer. You can write S.Memory^ directly.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    Thanks for the hint, I thought that if console is empty it's not working, but redirecting stdout to a file ("appname.exe > out.jpg") worked out just fine. – Kromster Mar 21 '13 at 08:56
  • 1
    On a sidenote: I'm working on an app that needs to generate and output text-to-speech into stdout, so yes, that is binary data and a lot of it (few Mb). But your answer covers many aspects, so please don't cut it, so that it is useful to others ) – Kromster Mar 21 '13 at 09:08
  • @KromStern: Your last comment makes no sense. If you're doing `text to speech to stdout` and then redirecting `stdout` to a file, you could simply do `text to speech to file` and cut out the `stdout` middleman. There's no point in writing to the console to immediately redirect that output to a file. – Ken White Mar 22 '13 at 01:44
  • @Ken White: I needed to write tts to stdout so that other programmer could rig it into his setup. I was testing stdout > file to check that my part is correct. – Kromster Mar 22 '13 at 05:13