Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Streaming a TStringList with associated objects

882 views
Skip to first unread message

Magnus S. Petersen

unread,
May 6, 2008, 2:44:15 AM5/6/08
to
Can anyone help me with this.
I have a combobox filled with stringvalues. Each stringitem has a small
object associated.
I want to save the combobox Items(TStrings) with the object to the disk and
of course also be able read them back from disk.
Can anyone provide me with a small example or point me to an article etc.

Best regards
Magnus


Remy Lebeau (TeamB)

unread,
May 6, 2008, 2:49:04 AM5/6/08
to

"Magnus S. Petersen" <mag...@post.olivant.fo> wrote in message
news:481f...@newsgroups.borland.com...

> I have a combobox filled with stringvalues. Each stringitem has a small
> object associated. I want to save the combobox Items(TStrings) with
> the object to the disk and of course also be able read them back from
> disk.

You have to manually loop through the Items reading/writing the file
yourself, since only you know what kind of objects are stored in the Items
to begin with.


Gambit


Brandon Staggs

unread,
May 6, 2008, 10:42:09 AM5/6/08
to

TStringList.Objects is just a list of pointers to memory locations --
Since they can point to anything (I even use them to just store
integers, which makes them invalid as TObjects) you have to write your
own code to handle streaming them.

--
Brandon Staggs
http://www.swordsearcher.com
http://www.studylamp.com
http://www.brandonstaggs.com

Magnus S. Petersen

unread,
May 8, 2008, 4:45:06 AM5/8/08
to
Thanks for your answer.
I was aware of that i had to write code on my own to stream the objects, but
i was looking for examples to do so as i am not so
aquainted with using TStreamList, TReader, TWriter etc.
I have an object named TStoneObj with some datafields and have implemented
two procedures to my simple object, procedure TStoneObj.Load(reader:
TReader) and TSToneObj.Save(writer: TWriter) where i use the
reader.ReadString and writer.WriteString for the string fields.
So i was looking for an example to guide me in my struggling. I suppose i
must loop through the StringList with a for loop, and retreive each
associated object and write to the stream.
Can you point me to some description of the streaming process ?
Best regards
Magnus

"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:481fff52$1...@newsgroups.borland.com...

Gerrit Beuze

unread,
May 8, 2008, 6:46:25 AM5/8/08
to
Hello Magnus,

> I was aware of that i had to write code on my own to stream the objects, but i was looking for examples to do so as i am not so
> aquainted with using TStreamList, TReader, TWriter etc.

Why not use xml rather than a binary stream?
Binary streams are har to maintain over time: you'll at least
need some kind of versioning to be able to deal with future changes

TReader and TWriter are only intended for streaming the dfm's.
I would not suggest using those.
I you go for binary, check for example
procedures TCustomTreeView.CreateWnd (reading) and
procedures TCustomTreeView.DestroyWnd (writing)
both in unit ComCtrls.pas

This shows how to create a (memory) stream and store / read some data on it.
Instead of a memorystream you could use a filestream, or even better:
store the memorystream to a filestream to get some kind of buffering.

But as said: I recommend plain text such as xml.

Gerrit Beuze
ModelMaker Tools

Boost your productivity in the Delphi IDE with ModelMaker Code Explorer:
http://www.modelmakertools.com/code-explorer/index.html


Remy Lebeau (TeamB)

unread,
May 8, 2008, 4:14:14 PM5/8/08
to

"Magnus S. Petersen" <mag...@post.olivant.fo> wrote in message
news:4822...@newsgroups.borland.com...

> I was aware of that i had to write code on my own to stream the objects,
> but i was looking for examples to do so

See further below.

> as i am not so aquainted with using TStreamList, TReader, TWriter etc.

Although you could use TReader and TWriter, I would suggest using TStream
directly instead. TReader and TWriter are primarily meant for DFM
streaming, which you are not using.

> So i was looking for an example to guide me in my struggling.

What exactly are you struggling with, though? To save the list, simply
write out the number of items in the list, then loop through the list
calling TStoneObj.Save() on each object. To load the list, simply read the
number of item, then loop that many times creating TStoneObj instances,
calling TStoneObj.Load() and AddObject() for each one. For example:

procedure WriteStringToStream(const S: String; Stream: TStream);
var
Len: Integer;
LS: UTF8String;
begin
// D2008 will have native UTF-8 support (finally)...
LS := {$IFDEF VER200}S{$ELSE}UTF8Encode(S){$ENDIF};
Len := Length(LS);
Stream.WriteBuffer(@Len, SizeOf(Len));
Stream.WriteBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
end;

procedure SaveItemsToStream(Items: TStrings; Stream: TStream);
var
Count, I: Integer;
begin
Count := Items.Count;
Stream.WriteBuffer(@Count, SizeOf(Count));
for I := 0 to Count-1 do
begin
WriteStringToStream(Items.Strings[I], Stream);
TStoneObj(Items.Objects[I]).Save(Stream);
end;
end;

procedure SaveItemsToFile(Items: TStrings; const FileName: String);
var
Strm: TFileStream;
begin
Strm := TFileStream.Create(FileName, fmCreate);
try
try
SaveItemsToStream(Items, Strm);
finally
Strm.Free;
end;
except
DeleteFile(FileName);
raise;
end;
end;

begin
SaveItemsToFile(ComboBox1.Items, 'myfile.dat');
end;


Then to load the Items back:

function ReadStringFromStream(Stream: TStream): String;
var
Len: Integer;
LS: UTF8String;
begin
Stream.ReadBuffer(@Len, SizeOf(Len));
SetLength(LS, Len);
if Len > 0 then
begin
Stream.ReadBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
// D2008 will have native UTF-8 support (finally)...
Result := {$IFDEF VER200}LS{$ELSE}UTF8Decode(LS){$ENDIF};
end else
Result := '';
end;

procedure ReadItemsFromStream(Items: TStrings; Stream: TStream);
var
Count, I: Integer;
S: String;
Obj: TStoneObj;
begin
Items.BeginUpdate;
try
Count := Items.Count;
for I := 0 to Count-1 do TStoneObj(Items.Objects[I]).Free;
Items.Clear;
Stream.ReadBuffer(@Count, SizeOf(Count));
for I := 0 to Count-1 do
begin
S := ReadStringFromStream(Stream);
Obj := TStoneObj.Create;
try
Obj.Load(Stream);
Items.AddObject(S, Obj);
except
Obj.Free;
raise;
end;
end;
finally
Items.EndUpdate;
end;
end;

procedure ReadItemsFromFile(Items: TStrings; const FileName: String);
var
Strm: TFileStream;
begin
Strm := TFileStream.Create(FileName, fmOpenRead or mShareDenyWrite);
try
ReadItemsFromStream(Items, Strm);
finally
Strm.Free;
end;
end;

begin
ReadItemsFromFile(ComboBox1.Items, 'myfile.dat');
end;

> I suppose i must loop through the StringList with a for loop, and retreive
> each associated object and write to the stream.

Yes.


Gambit


Magnus S. Petersen

unread,
May 10, 2008, 7:20:54 AM5/10/08
to
Dear Gerrit Beuze
Thank you very much for your answer.
I have used the TReader and TWriter some times and found them easy to work
with, but i understand now what they intended for.
I will not use them now for such jobs as the one i have described. Thanks.
XML is also an area where i am rather lost, but apparently i have got to
give it a study.
Your reference to the ComCtrls.pas unit with the procedures
TCustomTreeView.CreateWnd and TCustomTreeView.DestroyWnd was very
instructive.
Thank you
Best regards
Magnus S Petersen


"Gerrit Beuze" <gerrit[at][-nospam]modelmakertools[dot]com> skrev i en
meddelelse news:4822da04$1...@newsgroups.borland.com...

Magnus S. Petersen

unread,
May 10, 2008, 7:28:04 AM5/10/08
to
Dear Remy Lebeau

Thank you very much for your answer.
This looks very much what i was looking for, so my "struggle" will be much
easier now.
Are there any good articles, tips etc. concerning the use of TStreams, you
can reccommend ?
Thanks once again

Best regards
Magnus S Petersen

"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:48235f34$1...@newsgroups.borland.com...

Magnus S. Petersen

unread,
May 12, 2008, 8:47:29 AM5/12/08
to
Dear Remy Lebeau
I still have difficulties in this streaming business.
Please find below my to objects. Can you have a look and finish the
streaming process.
I know that it is much to ask, but i am stuck.
Type
TStoneObj = class
private
FName,
FPseudonym,
FPictureName,
//FPicture:TImage;
FStoneType,
FCountry,
FColor,
FPicture,
FOnStock,
FPrice:string;
published
procedure Load;
procedure Save;
property Name:string read FName write Fname;
property Pseudonym:string read FPseudonym write FPseudonym;
property PictureName:string read FPictureName write FPictureName;
property StoneType:string read FStoneType write FStoneType;
property Country:string read FCountry write FCountry;
property Color:string read FColor write FColor;
property Picture:string read FPicture write FPicture;
property OnStock:string read FOnStock write FOnStock;
property Price:string read FPrice write FPrice;
end;

TStoneListObj = class
private
FStoneList:TStringList;
FStoneObj:TStoneObj;
destructor destroy;
published
constructor create;
procedure AddNewStone;
procedure ChangeStone;
procedure DeleteStone;
procedure SaveStoneListToStream;
procedure LoadStoneListFromStream;
property StoneList:TStringList read FStoneList;
end;

var
StoneObj:TStoneObj;StoneListObj:TStoneListObj;

{------------------------------------------------------------------}
{ Implementing the TStoneObject used in program }
{------------------------------------------------------------------}
procedure TStoneObj.Load;
begin
// ?????
end;

procedure TStoneObj.Save;
begin
// ?????
end;
{------------------------------------------------------------------}
{ Implementing the StoneList object used in program }
{------------------------------------------------------------------}
constructor TStoneListObj.create;
begin
inherited create;
FStoneList:=TstringList.Create;
FStoneObj:=TStoneObj.Create;
end;

destructor TStoneListObj.destroy;
begin
FStoneList.free;
FStoneObj.free;
end;

procedure TStoneListObj.AddNewStone;
var
aStoneObj:TStoneObj;
begin
{First we create the stone object}
aStoneObj:=TStoneObj.Create;
with aStoneObj do
begin
Name :=MainForm.cbxStoneName.Text;
Pseudonym :=MainForm.memPseudonyms.Lines.CommaText;
PictureName :=MainForm.cbxPictureName.Text;
if MainForm.rgStoneType.ItemIndex<>-1 then
StoneType
:=MainForm.rgStoneType.Items[MainForm.rgStoneType.ItemIndex];
Country :=MainForm.cbxCountry.Text;
if MainForm.rgColor.ItemIndex<>-1 then
Color :=MainForm.rgColor.Items[MainForm.rgColor.ItemIndex];
//Picture :=
OnStock :=IntToStr(MainForm.rgOnStock.ItemIndex);
Price :=MainForm.edPrice.Text;
end;
{Second we add the stone object to the list}
FStoneList.AddObject(aStoneObj.Name,aStoneObj);
end;//Works fine.

procedure TStoneListObj.ChangeStone;
begin
//implement later
end;

procedure TStoneListObj.DeleteStone;
begin
//implement later
end;

procedure TStoneListObj.LoadStoneListFromStream;
begin
//
end;

procedure TStoneListObj.SaveStoneListToStream;
begin
//
end;

Thank you very, very much in advance.
Best regards
Magnus

"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse

news:48235f34$1...@newsgroups.borland.com...

Magnus S. Petersen

unread,
May 12, 2008, 8:57:21 AM5/12/08
to
I am using CodeGearT RAD Studio 2007, enterprise edition.
Magnus

"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse

news:48235f34$1...@newsgroups.borland.com...

Remy Lebeau (TeamB)

unread,
May 12, 2008, 2:16:04 PM5/12/08
to

"Magnus S. Petersen" <mag...@post.olivant.fo> wrote in message
news:4828...@newsgroups.borland.com...

> I still have difficulties in this streaming business.

Again, what EXACTLY are you having problems with? You never answered that
question when I asked you earlier.

> Please find below my to objects. Can you have a look
> and finish the streaming process.

Asside from the syntax errors in your code, you are not even close to
implementing the streaming properly. Did you even try the code I gave you?
For example:

type
TStoneObj = class
public
Name,
Pseudonym,
PictureName,
StoneType,
Country,
Color,
Picture,
OnStock,
Price: String;

procedure LoadFromStream(AStream: TStream);
procedure SaveToStream(AStream: TStream);
end;

TStoneListObj = class
private
FStoneList: TStringList;

...
public
constructor Create;
destructor Destroy; override;
...
procedure SaveToStream(AStream: TStream);
procedure LoadFromStream(AStream: TStream);
...
end;


function ReadStringFromStream(Stream: TStream): String;
var
Len: Integer;
LS: UTF8String;
begin
Stream.ReadBuffer(@Len, SizeOf(Len));
SetLength(LS, Len);
if Len > 0 then
begin
Stream.ReadBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
// D2008 will have native UTF-8 support (finally)...
Result := {$IFDEF VER200}LS{$ELSE}UTF8Decode(LS){$ENDIF};
end else
Result := '';
end;

procedure WriteStringToStream(const S: String; Stream: TStream);


var
Len: Integer;
LS: UTF8String;
begin
// D2008 will have native UTF-8 support (finally)...
LS := {$IFDEF VER200}S{$ELSE}UTF8Encode(S){$ENDIF};
Len := Length(LS);
Stream.WriteBuffer(@Len, SizeOf(Len));
Stream.WriteBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
end;


procedure TStoneObj.LoadFromStream(AStream: TStream);
begin
Name := ReadStringFromStream(AStream);
Pseudonym := ReadStringFromStream(AStream);
PictureName := ReadStringFromStream(AStream);
StoneType := ReadStringFromStream(AStream);
Country := ReadStringFromStream(AStream);
Color := ReadStringFromStream(AStream);
Picture := ReadStringFromStream(AStream);
OnStock := ReadStringFromStream(AStream);
Price := ReadStringFromStream(AStream);
end;

procedure TStoneObj.SaveToStream(AStream: TStream);
begin

WriteStringToStream(Name, AStream);
WriteStringToStream(Pseudonym, AStream);
WriteStringToStream(PictureName, AStream);
WriteStringToStream(StoneType, AStream);
WriteStringToStream(Country, AStream);
WriteStringToStream(Color, AStream);
WriteStringToStream(Picture, AStream);
WriteStringToStream(OnStock, AStream);
WriteStringToStream(Price, AStream);
end;


constructor TStoneListObj.Create;
begin
inherited Create;
FStoneList := TStringList.Create;
...
end;

destructor TStoneListObj.Destroy;
begin
...
FStoneList.Free;
inherited Destroy;
end;

procedure TStoneListObj.LoadFromStream(AStream: TStream);
var
Count, I: Integer;
aStoneObj: TStoneObj;
begin
FStoneList.BeginUpdate;
try
Count := FStoneList.Count;
for I := 0 to Count-1 do TStoneObj(FStoneList.Objects[I]).Free;
FStoneList.Clear;
AStream.ReadBuffer(@Count, SizeOf(Count));


for I := 0 to Count-1 do
begin

aStoneObj := TStoneObj.Create;
try
aStoneObj.LoadFromStream(AStream);
FStoneList.AddObject(aStoneObj.Name, aStoneObj);
except
aStoneObj.Free;
raise;
end;
end;
finally
FStoneList.EndUpdate;
end;
end;

procedure TStoneListObj.SaveToStream(AStream: TStream);


var
Count, I: Integer;
begin

Count := FStoneList.Count;
AStream.WriteBuffer(@Count, SizeOf(Count));


for I := 0 to Count-1 do

TStoneObj(FStoneList.Objects[I]).SaveToStream(Stream);
end;


Gambit


Magnus S. Petersen

unread,
May 12, 2008, 4:26:54 PM5/12/08
to
Dear Remy Lebeau
Thank you for your speedy and qualified answer with the attached code. I am
very grateful
It was not my intention to insult you in any way. I removed my code where i
had tried to implement the streaming, before i sent the objects to you and
yes there were several syntax errors.
I have implemented your code. My objects reside in a separate unit named
ObjectUnit. In the interface section uses clause i have Classes and
SysUtils.
When i compile the code now, with the implementation of the streaming
process, i get an error on the @ operator in the function

function ReadStringFromStream(Stream: TStream): String;
var
Len: Integer;
LS: UTF8String;
begin
Stream.ReadBuffer(@Len, SizeOf(Len));

[DCC Error] ObjectUnit.pas(94): E2197 Constant object cannot be passed as
var parameter

Is there a compiler directive i have missed or what do you think is the
reason for not accepting the pointer reference

Best regards
Magnus

"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse

news:48288985$1...@newsgroups.borland.com...

Pieter Zijlstra

unread,
May 12, 2008, 5:51:45 PM5/12/08
to
Magnus S. Petersen wrote:

> When i compile the code now, with the
> implementation of the streaming process, i get an error on the @
> operator in the function
>

> Stream.ReadBuffer(@Len, SizeOf(Len));
>
> [DCC Error] ObjectUnit.pas(94): E2197 Constant object cannot be
> passed as var parameter
>
> Is there a compiler directive i have missed or what do you think is
> the reason for not accepting the pointer reference

Remove the "@" and it will compile.

The same applies to the ones in WriteStringToStream, LoadFromStream and
SaveToStream.

--
Pieter

Magnus S. Petersen

unread,
May 12, 2008, 6:12:50 PM5/12/08
to
Thanks once again.
Magnus
"Pieter Zijlstra" <p.zylstr...@hccnet.nl> skrev i en meddelelse
news:xn0fq3wqe...@newsgroups.codegear.com...

Magnus S. Petersen

unread,
May 12, 2008, 7:15:23 PM5/12/08
to
Dear Pieter Zijlstra/Remy Lebeau
The streaming works perfect.
Thank you very, very much for the assistance.
Magnus

"Pieter Zijlstra" <p.zylstr...@hccnet.nl> skrev i en meddelelse
news:xn0fq3wqe...@newsgroups.codegear.com...

0 new messages