There's not much point going into detail about "where" your code goes
wrong. Writing custom "text devices" is a bit of a black art that has
not been used widely since the DOS days, and not documented properly
ever since in the apparent hope it will vanish from disuse.
Firstly I don't think you'll be able to redirect the output "stdout"
to a file since AFAIK stdout is not relevant in UI programs. But all
that means though is that Write and Writeln will need to be modified
slightly to write to your custom text device like this: Writeln(memo,
blah1, blah2) instead of just Writeln(blah1, blah2), but that
shouldn't be too much of a problem for you.
Secondly, I would say that a TMemo is not ideal for modelling as a
text device since it's not straight forward to write arbitrary
partial text chunks to it. eg. Write(memo, buffer, count)
However, as an intermediary step, I have a unit that has an
AssignStream procedure. This handy unit enables *any* TStream
descendent that contains text to be written to and read from using
traditional Readln's and Writeln's. eg.
var
strm: TFileStream;
f: System.Text;
strOut: string;
begin
strm := TFileStream.Create('myfile.txt', fmOpenRead);
AssignStream(f, strm);
Reset(f);
while not Eof(f) do
begin
Readln(f, strOut);
{ do something with strOut }
end;
Close(f);
strm.Free;
end;
Although the example above uses the TFileStream descendant any
TStream descendant containing text could have been used (ie. assigned
to the strm variable) and used the same way.
Also, any writeable stream can be used to write text to using
Write/Writeln by using Rewrite(f) instead of Reset.
You might be thinking: "But I need the text device to represent a
TMemo not a TStream, so how does that help me?".
The answer to that is that it is relatively *easy* to model a TMemo
to a TStream descendant, at least much easier than it is to directly
model it as a text device.
Here's code that will do that:
unit MemoStrm;
interface
uses SysUtils, Classes, StdCtrls;
type
TMemoStream = class(TStream)
private
m: TMemo;
public
constructor Create(memo: TMemo);
function Write(const Buffer; Count: longint): longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
{ I've left out overriding Read since it's not really workable
to be reading from a TMemo as though it is an input text device,
but it could be possible I suppose. }
end;
implementation
constructor TMemoStream.Create(memo: TMemo);
begin
inherited Create;
m := memo;
end;
function TMemoStream.Write(const Buffer; Count: longint): longint;
var
newstr: PChar;
begin
newstr := StrAlloc(count + 1);
Move(Buffer, newstr^, count);
newstr[count] := #0;
m.SetSelTextBuf(newstr);
StrDispose(newstr);
Result := Count;
end;
function TMemoStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
soFromBeginning:
m.Selstart := Offset;
soFromCurrent:
m.Selstart := m.SelStart + Offset;
soFromEnd:
m.Selstart := m.GetTextLen - Offset;
end;
Result := m.Selstart;
end;
end.
Using this, and the TextStrm unit (attached below), you can write to
a TMemo as though it was a text file like this:
uses MemoStrm, TextStrm;
var
memostrm: TMemoStream;
fOut: System.Text;
begin
{ Initial stuff written to beginning of program }
memostrm := TMemoStream.Create(Memo1); // Memo1 is some component
AssignStream(fOut, memostrm);
System.Rewrite(fOut);
{ Output processed by the program }
Writeln(fOut, 'First line', 999);
Write(fOut, '1st bit'); Writeln(fOut, '2nd bit');
{ Remember to do this at the end of the program }
System.Close(fOut);
memostrm.Free;
end;
=== UNIT TEXTSTRM.PAS =======
unit TextStrm;
(* AssignStream procedure enables any TStream descendant that
stores text data to be read or written to as a text device *)
interface
uses SysUtils, Classes;
procedure AssignStream(var F: System.Text; stream: TStream);
{Stream must already be opened before assigning to Text }
implementation
type
PStream = ^TStream;
function OutputText(var F: TTextRec): Integer;
begin
with F do
begin
PStream(
UserData)^.WriteBuffer(BufPtr^, BufPos);
BufPos:=0;
OutputText := 0;
end;
end;
function InputText(var F: TTextRec): integer;
begin
with F do
begin
BufEnd := PStream(
UserData)^.Read(BufPtr^, BufSize);
BufPos := 0;
InputText := 0;
end;
end;
function FlushInput(var F: TTextRec): integer;
begin
with F do
begin
BufPos := 0;
BufEnd := 0;
end;
end;
function Ignore(var F: TTextRec): integer;
begin
Ignore := 0;
end;
function OpenTextStream(var F: TTextRec): Integer;
begin
with F do
begin
case Mode of
fmInput:
begin
InOutFunc:=
InputText;
FlushFunc:=
FlushInput;
with PStream(
UserData)^ do Position := 0;
end;
fmInOut:
begin
Mode:=fmOutput;
InOutFunc:=
OutputText;
FlushFunc:=
Ignore; // buffered. Use
OutputText for
unbuffered.
with PStream(
UserData)^ do Position := Size;
end;
fmOutput:
begin
Mode:=fmOutput;
InOutFunc:=
OutputText;
FlushFunc:=
Ignore; // buffered. Use
OutputText for
unbuffered.
with PStream(
UserData)^ do Position := 0;
end;
end; {case Mode}
CloseFunc:=
Ignore;
BufPos := 0;
BufEnd := 0;
end;
OpenTextStream := 0;
end;
procedure AssignStream(var F: System.Text; Stream: TStream);
begin
with TTextRec(F) do
begin
Mode:=fmClosed;
BufSize:=SizeOf(Buffer);
BufPtr:=
Buffer;
OpenFunc:=
OpenTextStream;
PStream(
UserData)^ := Stream;
Name[0]:=#0;
end;
end;
end.
--- In delphi-en%40yahoogroups.com">delphi-en
yahoogroups.com, "tim11g" <kc0fcp
...> wrote:
>
> I have some old code filled with Writeln's that I want to use in a
GUI
> application. I would like to redirect STDOUT to a Tmemo, so the
> writeln's would be logged into the Memo.
>
> Modeling on the code for CRT32, I have written a a replacement for