0

I am using TMemoryStream to send dynamic records using indy TCPServer.

The structure of my record is as follows:

TMyRecord = record
  name : string ;  // unassigned size
  ID : integer ;
  Picture : TJPEGImage ;  
end;

How can I assign the variable of this record type into a TMemoryStream? Also please explain me how to extract this TMemoryStream into record type.

LU RD
  • 34,438
  • 5
  • 88
  • 296
Vibeeshan Mahadeva
  • 7,147
  • 8
  • 52
  • 102
  • 3
    This is a general problem known as persistence. You can do it yourself using raw stream `WriteBuffer` and `ReadBuffer` calls. You need to write the length of the string, followed by its contents. I'd use UTF8 for persistence. You then need to write the integer and then you need to call `Picture.SaveToStream` to get the JPEG to put itself to the stream. Reverse the process when reading. You really don't want to write this sort of code on a daily basis. Ideally you need to use a higher level abstraction. You can write that yourself or even better use a persistence framework. – David Heffernan Nov 30 '11 at 12:24
  • 2
    See also: [saving a records containing a member of type string to a file (Delphi, Windows)](http://stackoverflow.com/q/1472325/243614),   [Delphi 2010: How to save a whole record to a file?](http://stackoverflow.com/q/3820996/243614),   [Writing complex records to file](http://stackoverflow.com/q/4533376/243614). – Sertac Akyuz Nov 30 '11 at 12:26

1 Answers1

3

The basic idea is

Tmyrecord = Record
  name : string ;  // unassaigned 
  ID : integer ;
  Picture : TJPEGImage ;
  procedure SaveToStream(const aStream: TStream);
  procedure LoadFromStream(const aStream: TStream);
end;

procedure Tmyrecord.SaveToStream(const aStream: TStream);
var Len: Integer;
begin
  // save the length of name str
  Len := Length(name);
  aStream.Write(Len, SizeOf(Len));
  // save the name
  if(Len > 0)then aStream.Write(name[1], Len * SizeOf(name[1]));
  // save the ID
  aStream.Write(ID, SizeOf(ID));
  // save image
  Picture.SaveToStream(aStream);
end;

procedure Tmyrecord.LoadFromStream(const aStream: TStream);
var Len: Integer;
begin
  // read the length of name str
  aStream.Read(Len, SizeOf(Len));
  if(Len > 0)then begin
     // read the name
     SetLength(name, Len);
     aStream.Read(name[1], Len * SizeOf(name[1]));
  end else name := '';
  // load the ID
  aStream.Read(ID, SizeOf(ID));
  // load image
  if(Picture = nil)then Picture := TJPEGImage.Create;
  Picture.LoadFromStream(aStream);
end;
ain
  • 22,394
  • 3
  • 54
  • 74
  • 1
    Wow... I was typing practically the exact same thing as you posted that! +1 :) – LaKraven Nov 30 '11 at 12:30
  • But in fact you want to avoid having to write such code by means of abstraction – David Heffernan Nov 30 '11 at 12:35
  • What happens when Picture is empty when saving to the stream? How will loading from such a stream behave? – Ondrej Kelle Nov 30 '11 at 12:46
  • 1
    @TOndrej Yes, if there is possibility that the `Picture` is unassigned one would want to add an flag to the strean to signal that - something similar to how empty string case is handled. – ain Nov 30 '11 at 12:55
  • 1
    I would save the `TPicture.Graphic` to a temporary `TStream`, then write that stream's `Size` to the `TMemoryStream` before copying the graphic content into it. That way, the receiver can check if the graphic data is 0 bytes, and if not than copy that many bytes into a temporary `TStream` before calling `TGraphic.LoadFromStream()`. – Remy Lebeau Nov 30 '11 at 22:27