We help IT Professionals succeed at work.

Write/Read Record to/from IniFile with WriteBinaryStream/ReadBinaryStream?

PeterDelphin
PeterDelphin asked
on
In Delphi (XE7) I need to store a record in an IniFile with WriteBinaryStream:

Pseudocode:

IniFile.WriteBinaryStream('ASection', 'AName', GetStream(MyRecord));

Open in new window


Then read the single record fields with a simple method like:

Pseudocode:

MyRecord := GetRecordFromStream(Inifile.ReadBinaryStream(...));
Value1 := MyRecord.ValueX;
Value2 := MyRecord.Valuey;

Open in new window


How can all this be done?
Comment
Watch Question

Top Expert 2011
Commented:
Writing a Record to Stream is not direct, check this link.

On the other hand you can use a StringList Values feature:
procedure TForm1.Button1Click(Sender: TObject);
var
  ii:Tinifile;
  memstr:TStream;
  strlst:TStrings;
begin
  memstr:=TMemoryStream.Create;
  strlst:=TStringList.Create;
  ii:=Tinifile.Create('test.ini');
  try
    ii.ReadBinaryStream('ASection', 'AName', memstr);
    strlst.LoadFromStream(memstr);

    showmessage(strlst.ValueFromIndex[0]); // show first value
    showmessage(strlst.Text); // View all the values
  finally
    ii.Free;
    memstr.Free;
    strlst.Free;
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  ii:Tinifile;
  memstr:TStream;
  strlst:TStrings;
begin
  memstr:=TMemoryStream.Create;
  strlst:=TStringList.Create;
  ii:=Tinifile.Create('test.ini');
  try
    strlst.Values['value1']:= 'Valuex';
    strlst.Values['value2']:= 'Valuey';
    strlst.SaveToStream(memstr);
    memstr.Position:= 0;

    ii.WriteBinaryStream('ASection', 'AName', memstr);
  finally
    ii.Free;
    memstr.Free;
    strlst.Free;
  end;
end;

Open in new window

Author

Commented:
With your methods I can store and read only strings. To store and read a stringlist to/from an inifile I wouldn't need WriteBinaryStream/ReadBinaryStream. That's why I have to use a record: Using different data types.
Top Expert 2011

Commented:
I understand. That was just an example of passing TStringList to Stream to ini.
But you could easily rework that with the code form SO link provided to add records.
On the linked page I found another link: https://code.google.com/p/kblib/

TKBDynamic allows to save/load/get(binary)size/compare any dynamic type with only one line of code. This is a very good library and perfect for saving a record to a stream. So I have created this code to achieve what was my question:

// Save record to IniFile:

memstr := TMemoryStream.Create;
try
    TKBDynamic.WriteTo(memstr, AMyRecord, TypeInfo(TMyRecord));
    memstr.Position := 0;
    IniFile.WriteBinaryStream(ASection, AName, memstr);
finally
    memstr.Free;
end;

// Read record From IniFile:

// Check whether Key exists to avoid StreamRead-Error:
if not IniFile.ValueExists(ASection, AName) then
    EXIT;
memstr := TMemoryStream.Create;
try
  IniFile.ReadBinaryStream(ASection, AName, memstr);
  memstr.Position := 0;
  TKBDynamic.ReadFrom(memstr, AMyRecord, TypeInfo(TMyRecord));
  Result := AMyRecord;
finally
  memstr.Free;
end;

Open in new window

Author

Commented:
This is the best solution for the initial question: It is easy to implement and allows to save records to an inifile.

Author

Commented:
Accept. This system is very confusing. I don't know whether I should Object or Object. What does this mean?
Sinisa VukSoftware architect
Top Expert 2012

Commented:
Even you close, ... I can suggest writing Base64 encoded binary record. It is quite easy to do this.

Author

Commented:
@Sinisa Vuk AFAIS, doesn't uKBDynamic just do this?
Sinisa VukSoftware architect
Top Expert 2012

Commented:
example at bottom (StreamToHexStr64)
http://www.delphipages.com/forum/showthread.php?t=154336

Author

Commented:
@Sinisa Vuk How does this relate to my question " store a record in an IniFile with WriteBinaryStream"? The question is not how to convert a stream to a string representation but how to convert a record to a stream.
Sinisa VukSoftware architect
Top Expert 2012

Commented:
this is my example:

uses ..., DCPbase64;

Type
  PMyRec = ^TMyRec;
  TMyRec = record
    ValueX: integer;
    ValueY: Integer;
  end;

procedure TForm1.Button5Click(Sender: TObject);
var
  ini: TIniFile;
  myrec: TMyRec;
  sRec: AnsiString;
begin
  myrec.ValueX := 200;
  myrec.ValueY := 100;

  ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    //make string long enough
    SetLength(sRec,((SizeOf(myrec)+2) div 3) * 4);
    //encode
    Base64Encode(@myrec, PAnsiChar(sRec), SizeOf(myrec));
    //write to ini
    ini.WriteString('Data', 'MyRec', sRec);
  finally
    ini.Free;
  end;
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  ini: TIniFile;
  myrec: TMyRec;
  sRec: AnsiString;
begin
  ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    //read from ini
    sRec := ini.ReadString('Data', 'MyRec', '');
    if Length(sRec) > 0 then
    begin
      //decode
      sRec := Base64DecodeStr(sRec);
      //set record
      if Length(sRec) = SizeOf(myrec) then
        CopyMemory(@myrec, PAnsiChar(sRec), SizeOf(myrec));
    end;
  finally
    ini.Free;
  end;
end;

Open in new window


...and for this example I use unit for base 64 encoding: DCPbase64.pas
Alternatively, you can use some other.

Author

Commented:
This is the best solution for the initial question: It is easy to implement and allows to save records to an inifile.