• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 312
  • Last Modified:

Delphi 2 bug. Has it been fixed yet?

I found a bug which may not have yet been detected and fixed by Borland because it relates to the old (and obscure) art of writing Pascal text device drivers.

The code that produces this bug is straight-forward enough.

procedure TForm1.Button1Click(Sender: TObject);
var
  strm: TStream;
  f: System.Text;
  aline: string;
  b: integer;
begin
  strm := TFileStream.Create('c:\sometext.txt', fmOpenRead);
  AssignStream(f, strm);
  Reset(f);
  b := TTextRec(f).BufPos;
  Readln(f, aline);
  CloseFile(f);
  strm.Free
end;

Commenting out the line "b := TTextRec(f).BufPos" makes everything work fine.

Here is the code for my AssignStream procedure.

unit TextStrm;
(* Written by: Ian Hinson (ihinson@ozemail.com.au) 22 January, 2000

   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(Buffer, BufPos);
    BufPos:=0;
    OutputText := 0;
  end;
end;

function InputText(var F: TTextRec): integer;
begin
  with F do
  begin
    BufEnd := PStream(@UserData)^.Read(Buffer, 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;
      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.
      end;
    end; {case Mode}
    CloseFunc:=@Ignore;
  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.

Work-a-round I have found which make the bug go away are:
1. Declare f as a global variable, not a local variable, or
2. Change the method to a procedure then call the procedure from the method.

My guess is that the reason it won't work (unless the workarounds are used) has something to do with compiler optimisations that make f inaccessible outside the method.

What I'm really after here I guess is for someone (with Delphi4/5) to give it a try and report back.  ..But if you can find a flaw in my code, please let me know.
0
IanHinson
Asked:
IanHinson
  • 7
  • 3
1 Solution
 
rwilson032697Commented:
Hmm.. Some observations/questions...

1. Shouldn't b be defined as cardinal?

2. The line B:=... has no effect in your onclick procedure...

3. Have you tried compiling the code without optimisation?

4. What goes wrong when you leave the code in place?

Cheers,

Raymond.
0
 
rwilson032697Commented:
I can confirm the 'bug' is still there in D5.

What I think is happening is BufPos is uninitialised... If I initialise it to 0 in the OpenTextStream it runs correctly...

Cheers,

Raymond.
0
 
rwilson032697Commented:
Actually, initialising it in AssignStream would be better :-)

Cheers,
Raymond.
0
Receive 1:1 tech help

Solve your biggest tech problems alongside global tech experts with 1:1 help.

 
IanHinsonAuthor Commented:
Initialising BufPos to zero renders the whole thing ineffective and causes Readln to return nothing - hence it 'appears' to work.
Apparently Pascal automatically initialises BufPos with a magic value which may mean 'position unknown'.

Anyway, thanks for your trouble in trying it and letting me know that the bug is still there.

If you get any further thoughts about what's causing it, please let me know.

Ian.

0
 
rwilson032697Commented:
How weird! We do use a text device driver in a piece of code at work - I'll take a look at it when I get in...

Cheers,

Raymond.
0
 
rwilson032697Commented:
I've check out our code, and we do initialise bufpos to 0 as I suggested (in openTextStream). I can't see any other really significant changes so I'm a bit mystified.

Cheers,

Raymond.
0
 
IanHinsonAuthor Commented:
There's an interesting statement in Charles Petzold's book 'Programming Windows 95' (pg780):
"Global variables in a multithreaded program (as well as any allocated memory) are shared among all the threads in the program." ..
"Local automatic variables in a function are unique to each thread, because they are stored on the stack, and each thread has its own stack."

Recall that I could make the 'bug' go away by declaring f:System.Text as a *global* variable?
Another workaround (I did not previously mention) is that by using:

var
  fp: ^System.Text // local
begin
  New(fp) // allocate memory on heap
.....
  Dispose(fp)
end;
...then the problem *also* does not appear.

Couple this with the error message that says there is an Access Violation at a memory address which is the correct address that I am after in the first place, and it fits in with what Petzold is saying.

I now think this is not a bug but a wake-up-call to the realities of Win32 programming.
My guess as to what's happening is that Pascal calls functions assigned to TTextRec in a separate thread. Therefore they cannot access var F if it has been declared on the stack of the calling thread.

Since the text device being used at your work is working OK, there is something I'm curious to know: Is var F (ie. at work) being declared as a global variable or a local variable?

Ian.
0
 
rwilson032697Commented:
Unless you have a multithreaded app (eg: you use the Win32 threading or TThread) then I would say you only have a sngle thread in your app...

Here's a code snippet from our app:

procedure TTextDisplayer.get_size (..parameters...);
   var
     line : Anystring;
     f : text;
....

begin
....

   AssignTextEntity(f,txt);
   reset(f);

   while not eof(f) do
     begin
     readln(f,line);
     ...
     end;
   close(f);
end;

This works just fine - always has.

Cheers,

Raymond.
0
 
IanHinsonAuthor Commented:
Eureka!!

This fix was as simple as adding the lines:
 BufPos := 0;
 BufEnd := 0;

into the OpenTextStream function.

Thanks again for pointing me in the right direction.  Couldn't have solved it without your feedback and advice.

Ian.
0
 
rwilson032697Commented:
Glad to help!

Cheers,

Raymond.
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 7
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now