EncodeString

This week I had to migrate an application from Delphi XE3 to Delphi Berlin and this application uses a Base64 encoding.

Normally Base64 encoding is not difficult stuff and Delphi XE3 simply offers the function EncodeString in the unit Soap.EncdDecd.

Since Delphi Berlin has the same function I thought that I didn’t have to change a single line of code. But the first thing I realized is that the function is nearly deprecated. This means that it already has a deprecated comment: // deprecated ‘Use TNetEncoding.Base64.Encode’.

Such a comment means that the function will become deprecated with one of the next Delphi releases.

A short look at the implementation shows the reason:

function EncodeString(const Input: string): string;
begin
  Result := TNetEncoding.Base64.Encode(Input);
end;

I should call TNetEncoding.Base64.Encode directly instead of using the old function EncodeString.

But then I realized that the two Delphi versions show different results.

Let’s have a look at a small example with some German umlauts:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(EncodeString('ÄäÖöÜüß'));
end;

Delphi XE3 shows ‘xOTW9tz83w==’ whereas Delphi Berlin shows ‘w4TDpMOWw7bDnMO8w58=’.

So bad luck, I had to have a deeper look at the code. Let’s start with Delphi XE3:

function EncodeString(const Input: string): string;
var
  InStr, OutStr: TStringStream;
begin
  InStr := TStringStream.Create(Input);
  try
    OutStr := TStringStream.Create('');
    try
      EncodeStream(InStr, OutStr);
      Result := OutStr.DataString;
    finally
      OutStr.Free;
    end;
  finally
    InStr.Free;
  end;
end;
 ...
 constructor TStringStream.Create(const AString: string);
 begin
   Create(AString, TEncoding.Default, False);
 end;

This means that the string is first converted into bytes with the help of the default encoding which is under Windows an ANSI encoding. After that the bytes are encoded.

Let’s have a look at the Delphi Berlin implementation:

function TNetEncoding.Encode(const Input: string): string;
begin
  Result := DoEncode(Input);
end;
...
function TBase64Encoding.DoEncode(const Input: string): string;
begin
  Result := DoEncodeBytesToString(TEncoding.UTF8.GetBytes(Input));
end;

Delphi Berlin always converts the string into UTF-8 bytes and then encodes the bytes. This means that the implementation of EncodeString in Delphi Berlin differs to the implementation in XE3.

To fix this issue let’s try the following implementation in Delphi Berlin:

procedure TForm1.Button1Click(Sender: TObject);
var
  lBytes: TBytes;
begin
  lBytes := TEncoding.Default.GetBytes('ÄäÖöÜüß');
  lBytes := TNetEncoding.Base64.Encode(lBytes);
  ShowMessage(TEncoding.Default.GetString(lBytes));
end;

I first convert the string into bytes with the help of the default encoding. Then I encode the bytes with the new Base64 class and at the end I have to convert the bytes back to a string.

I will contact Embarcadero/Idera and ask them if I should write a quality portal case.

This entry was posted in C++-Builder, Delphi, FireMonkey, RAD Studio 10 Seattle, RAD Studio 10.1 Berlin, RAD Studio XE5, RAD Studio XE6, RAD Studio XE7, RAD Studio XE8, Tips and Tricks, Uncategorized. Bookmark the permalink.