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

Icon and TStream

Hi,

It's the first time I'm using TStream.
I got an error when I try to save the Icon part of my object.
Find here below the code sample to help you to understand better...

Thank you in advance
Bob

type
  TStartOptions = (soSW_SHOW, soSW_SHOWNORMAL);

{************************************************************************}
  TRBLauncher = class
  private
    Command:             String;
    DefaultDir:          String;
    Parameters:          String;
    StartOption:         TStartOptions;
    procedure            SaveToStream  (Stream: TStream);
    procedure            LoadFromStream(Stream: TStream);
  public
    Caption:             String;
    Icon:                TIcon;
    Constructor          Create;
    Destructor           Destroy; override;
  end;
{************************************************************************}
  TRBLaunchList = class(TList)
  private
    procedure            SaveToStream(Stream: TStream);
    procedure            LoadFromStream(Stream: TStream);
    procedure            LoadFromFile(const FileName: string);
    procedure            SaveToFile(const FileName: string);
  public
    IconList:            TImageList;
    Constructor          Create;
    Destructor           Destroy; override;
    procedure            AddLauncher;
    procedure            EditLauncher(Index: integer);
    procedure            RemoveLauncher(Index: integer);
    procedure            ExecuteLauncher(Index: integer);
  end;
{************************************************************************}
procedure WriteStringToStream(const Stream: TStream; const Str: String);
var
  ALength: cardinal;
begin
  ALength:= Length(Str);
  Stream.Write(ALength, SizeOf(Cardinal));
  Stream.Write(Pointer(Str)^, ALength);
end;
{------------------------------------------------------------------------}
function ReadStringFromStream(const Stream: TStream): String;
var
  ALength: cardinal;
begin
  Result:= '';
  Stream.Read(ALength, SizeOf(Cardinal));
  SetLength(Result, ALength);
  Stream.Read(Pointer(Result)^, ALength);
end;
{************************************************************************}
Constructor TRBLauncher.Create;
begin
  Inherited Create;
  Icon:= TIcon.Create;
end;
{------------------------------------------------------------------------}
Destructor TRBLauncher.Destroy;
begin
  inherited Destroy;
end;
{------------------------------------------------------------------------}
procedure TRBLauncher.SaveToStream(Stream: TStream);
begin
    WriteStringToStream(Stream, Caption);
    WriteStringToStream(Stream, Command);
    WriteStringToStream(Stream, DefaultDir);
    WriteStringToStream(Stream, Parameters);
    Stream.Write(StartOption, SizeOf(StartOption));
    Icon.SaveToStream(Stream);  <---ERROR
end;
{------------------------------------------------------------------------}
procedure TRBLauncher.LoadFromStream(Stream: TStream);
begin
  Caption   := ReadStringFromStream(Stream);
  Command   := ReadStringFromStream(Stream);
  DefaultDir:= ReadStringFromStream(Stream);
  Parameters:= ReadStringFromStream(Stream);
  Stream.Read(StartOption, SizeOf(StartOption));
  Icon.LoadFromStream(Stream);
end;
{************************************************************************}
Constructor TRBLaunchList.Create;
begin
  Path := ExtractFilePath(ParamStr(0));
  IconList:= TImageList.CreateSize(32, 32);
  IconList.ShareImages:= false;
  Inherited Create;
  LoadFromFile(Path + DataFileName);
end;
{------------------------------------------------------------------------}
Destructor TRBLaunchList.Destroy;
var
  i: integer;
begin
  SaveToFile(Path + DataFileName);
  for i := 0 to Count - 1 do TRBLauncher(Items[i]).Free;
  IconList.Free;
  inherited Destroy;
end;
{------------------------------------------------------------------------}
procedure TRBLaunchList.SaveToStream(Stream: TStream);
var
  NumLauncher, i: integer;
begin
  NumLauncher := Count;
  Stream.Write(NumLauncher, sizeof(Integer));
  for i := 0 to Count - 1 do
    TRBLauncher(Items[i]).SaveToStream(Stream);
end;
{------------------------------------------------------------------------}
procedure TRBLaunchList.LoadFromStream(Stream: TStream);
var
  NumLauncher, i: integer;
  ALauncher: TRBLauncher;
  ByteRead  : LongInt;
begin
  for i := 0 to Count - 1 do
    TRBLauncher(Items[i]).Free;
  Clear;
  Stream.Read(NumLauncher, sizeof(Integer));
  for i := 0 to NumLauncher - 1 do
  begin
    ALauncher := TRBLauncher.Create;
    ALauncher.LoadFromStream(Stream);
    Add(ALauncher);
  end;
end;
{------------------------------------------------------------------------}
procedure TRBLaunchList.LoadFromFile(const FileName: string);
var
  Stream: TFileStream;
begin
  if FileExists(Filename) then
    begin
      Stream := TFileStream.Create(FileName, fmOpenRead);
      try
        LoadFromStream(Stream);
      finally
        Stream.Free;
      end;
  end;
end;
{------------------------------------------------------------------------}
procedure TRBLaunchList.SaveToFile(const FileName: string);
var
  Stream: TFileStream;
begin
  Stream := TFileStream.Create(FileName, fmCreate);
  try
    SaveToStream(Stream);
  finally
    Stream.Free;
  end;
end;
{************************************************************************}
0
baudewyns
Asked:
baudewyns
1 Solution
 
rwilson032697Commented:
I'd have to say that your Icon is not valid. All you have done is create it, but it does not look as though anything is ever assigned into it...

Cheers,

Raymond.
0
 
MadshiCommented:
What kind of error do you get? An exception? Or does it simply not work? How about a more detailed error message!?

Regards, Madshi.
0
 
baudewynsAuthor Commented:
To Raymond,

Don't be confused, I gave you only the part of the code related to the Stream. But the Icon is used elsewhere and working just fine. I only have a problem when I have to save my object into a file. When I remove the faulty line concerning the icon, everything is OK.

To Madshi,
Sorry, I forgot to mention it:
EExternalError...
0
[Webinar] Kill tickets & tabs using PowerShell

Are you tired of cycling through the same browser tabs everyday to close the same repetitive tickets? In this webinar JumpCloud will show how you can leverage RESTful APIs to build your own PowerShell modules to kill tickets & tabs using the PowerShell command Invoke-RestMethod.

 
MadshiCommented:
Usually I'm saving icons and bitmaps and imageLists and such stuff in their own TMemoryStream, then I save the length and content of this memory stream to the real stream (like you do with the strings). I've never had any problems with that. So I think it's worth a try for you, too.

Regards, Madshi.
0
 
baudewynsAuthor Commented:
Hi again Madshi,

As already mentionned, I don't have experience with TStream and TMemoryStream. What do you suggest to modify my code...

Best Regards, Bob
0
 
MadshiCommented:
Something like this:

procedure TRBLauncher.SaveToStream(Stream: TStream);
var ms : TMemoryStream;
    i1 : integer;
begin
    WriteStringToStream(Stream, Caption);
    WriteStringToStream(Stream, Command);
    WriteStringToStream(Stream, DefaultDir);
    WriteStringToStream(Stream, Parameters);
    Stream.Write(StartOption, SizeOf(StartOption));
    ms := TMemoryStream.Create;
    try
      Icon.SaveToStream(ms);
      i1 := ms.Size;
      Stream.Write(i1, 4);
      Stream.Write(ms.memory^, i1);
    finally ms.Free end;
end;

Regards, Madshi.
0
 
baudewynsAuthor Commented:
Well, I found the same problem with your code than with mine. So I investigate the code where the icon was assigned:

MyLauncher.Icon := AnyObject.Icon;

The line was changed to

MyLauncher.Icon.assign(AnyObject.Icon);

and...it's working.

I was thinking that the two lines are equivalent but it seems to be not the case.

But now, if I have more than one TRBLauncher object in the TRBLaunchList, then only the first one can be correctly read back from the stream. What I have to do in between when I read all objects stored in the stream ?

Regards, Bob
0
 
baudewynsAuthor Commented:
Adjusted points to 100
0
 
MadshiCommented:
Hi Bob,

try my suggestion again (using a temporarily memory stream), originally I used it exactly because of the problem you have now. If you call TIcon.LoadFromStream, this method reads the stream to its end, so after reading the first icon, everything is over.

Regards, Madshi.
0
 
baudewynsAuthor Commented:
Hi again Madshi,

I wrote the following code to read the Icon back from the Stream:

ms := TMemoryStream.Create;
Stream.Read(i1, 4);
ms.SetSize(i1);
try
  Stream.Read(ms.memory^, ms.Size);
  Icon.ReadFromStream(ms);
finally
  ms.Free
end;

and it raise an error EInvalidGraphic
"Icon image is not valid"

What's wrong ?

Regards, Bob
0
 
MadshiCommented:
Strange, it looks alright. (Except that you surely used LoadFromStream, not ReadFromStream, right?).
Try this, but I don't think it will make any difference:

  Stream.Read(ms.memory^, ms.Size);
  ms.Position := 0;
  Icon.LoadFromStream(ms);

Perhaps try this:
 
  TIcon.Create.LoadFromStream(ms);

Same error?

Regards, Madshi.
0
 
RadlerCommented:
Hi baudewyns;

I noticed that you wants a persistent class, why not inheriterance use, some like TComponent or TPersistent.
To TComponent are directs methods to use Save/Load( ToStream ).
If desired I provide some sample.

T++, Radler.
0
 
baudewynsAuthor Commented:
To Madshi,

As expected, the result remains the same
with the two propositions.

Hi Radler,
Welcome aboard.
Yes. If you have any solution to help me, please ...

What can I do now ?
Increase the prize money...

Regards
Bob
0
 
baudewynsAuthor Commented:
Adjusted points to 200
0
 
baudewynsAuthor Commented:
I will be far away next week. Don't expectd any feedback from me until Friday the 18th.

But, please, don't hesitate to let me any suggestion. I still need some help from all of you.

Regards, Bob
0
 
MadshiCommented:
Don't know what's wrong with your code, but this code works perfectly:

procedure SaveToStream(str1: string; ico: TIcon; str2: string);
var i1 : integer;
    ms : TMemoryStream;
begin
  with TFileStream.Create('c:\test.dat', fmCreate) do
    try
      i1 := Length(str1);
      Write(i1, 4);
      Write(pointer(str1)^, i1);
      ms := TMemoryStream.Create;
      try
        ico.SaveToStream(ms);
        i1 := ms.Size;
        Write(i1, 4);
        Write(ms.Memory^, ms.Size);
      finally ms.Free end;
      i1 := Length(str2);
      Write(i1, 4);
      Write(pointer(str2)^, i1);
    finally Free end;
end;

function LoadFromStream(var str1, str2: string) : TIcon;
var i1 : integer;
    ms : TMemoryStream;
begin
  with TFileStream.Create('c:\test.dat', fmOpenRead) do
    try
      Read(i1, 4);
      SetLength(str1, i1);
      Read(pointer(str1)^, i1);
      ms := TMemoryStream.Create;
      try
        Read(i1, 4);
        ms.Size := i1;
        Read(ms.Memory^, i1);
        result := TIcon.Create;
        result.LoadFromStream(ms);
      finally ms.Free end;
      Read(i1, 4);
      SetLength(str2, i1);
      Read(pointer(str2)^, i1);
    finally Free end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var s1, s2 : string;
    ico    : TIcon;
begin
  ico := TIcon.Create;
  ico.LoadFromFile('C:\test.ico');  // give in a valid ico file here
  SaveToStream('test1', ico, 'test2');
  ico.Free;
  ico := LoadFromStream(s1, s2);
  DrawIcon(Canvas.Handle, 0,0, ico.handle);
  ico.Free;
end;

Regards, Madshi.
0
 
baudewynsAuthor Commented:
Hi Madshi,

I'm back...
I decided to write completely the procedure based on your code and it works now. The error was quiet simple.
I forgot to read back the icon size. So the data was not corresponding to the Icon structure...

Thanks for all.
You get the points.

Bob
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

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