2

In Delphi 10.4, I try to save a valid TPicture base64-encoded to an INI file:

procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
var
  LInput: TMemoryStream;
  LOutput: TMemoryStream;
  MyIni: TIniFile;
  ThisFile: string;
begin
  if FileSaveDialog1.Execute then
    ThisFile := FileSaveDialog1.FileName
  else EXIT;

  LInput := TMemoryStream.Create;
  LOutput := TMemoryStream.Create;
  try
    APicture.SaveToStream(LInput);
    LInput.Position := 0;
    TNetEncoding.Base64.Encode(LInput, LOutput);
    LOutput.Position := 0;

    MyIni := TIniFile.Create(ThisFile);
    try
      MyIni.WriteBinaryStream('Custom', 'IMG', LOutput); // Exception# 234
    finally
      MyIni.Free;
    end;
  finally
    LInput.Free;
    LOutput.Free;
  end;
end;

WriteBinaryStream creates an exception:

ERROR_MORE_DATA 234 (0xEA) More data is available.

Why? What does this mean? How can this problem be solved?

EDIT: Taking into consideration what @Uwe Raabe and @Andreas Rejbrand said, this code (which does not use base64-encoding) now works:

procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
var
  LInput: TMemoryStream;
  MyIni: System.IniFiles.TMemIniFile;
  ThisFile: string;
begin
  if FileSaveDialog1.Execute then
    ThisFile := FileSaveDialog1.FileName
  else EXIT;

  LInput := TMemoryStream.Create;
  try
    APicture.SaveToStream(LInput);
    LInput.Position := 0;

    MyIni := TMemIniFile.Create(ThisFile);
    try
      MyIni.WriteBinaryStream('Custom', 'IMG', LInput);
      MyIni.UpdateFile;
    finally
      MyIni.Free;
    end;
  finally
    LInput.Free;
  end;
end;
user1580348
  • 5,721
  • 4
  • 43
  • 105
  • 1
    You should either use WriteBinaryStream without Base64 encoding or encode to Base64 and use WriteString instead. Base64 is already a textual representation. Taking that as binary stream doubles the memory needed. – Uwe Raabe Aug 02 '20 at 12:45
  • @UweRaabe: Not to mention that the result string *isn't even the Base64-encoded version of the image*! – Andreas Rejbrand Aug 02 '20 at 13:32
  • (Just note that the current solution doesn't actually use Base64.) – Andreas Rejbrand Aug 02 '20 at 13:53

1 Answers1

2

I believe this is a limitation in the operating system's functions for handling INI files; the string is too long for it.

If you instead use the Delphi INI file implementation, TMemIniFile, it works just fine. Just don't forget to call MyIni.UpdateFile at the end.

Yes, this is indeed a limitation in the Windows API, as demonstrated by the following minimal example:

var
  wini: TIniFile;
  dini: TMemIniFile;
begin

  wini := TIniFile.Create('C:\Users\Andreas Rejbrand\Desktop\winini.ini');
  try
    wini.WriteString('General', 'Text', StringOfChar('W', 10*1024*1024));
  finally
    wini.Free;
  end;

  dini := TMemIniFile.Create('C:\Users\Andreas Rejbrand\Desktop\pasini.ini');
  try
    dini.WriteString('General', 'Text', StringOfChar('D', 10*1024*1024));
    dini.UpdateFile;
  finally
    dini.Free;
  end;

(Recall that INI files were initially used to store small amounts of configuration data in the 16-bit Windows era.)

Also, Uwe Raabe is right: you should save the Base64 string as text.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384