Wodzu
Wodzu

Reputation: 6979

How writing to MemoryStream has changed in Delphi 10.2?

I am porting my code from 10.1 to 10.2 and this gives me error:

procedure TForm4.FormCreate(Sender: TObject);
const
  CFourBytes: array[0..3] of Byte = (1, 2, 3, 4);
var
  LStream: TMemoryStream;
  LBuffer: array of Byte;
begin
  SetLength(LBuffer, 4);
  LStream := TMemoryStream.Create;
  LStream.Write(@CFourBytes[0], 4); // E2036 Variable required

  LStream.Position := 0;
  LStream.ReadData(@LBuffer[0], 4);
end;

I had to change offending line to LStream.Write(CFourBytes[0], 4);

What has changed? Have I been doing it wrong for the whole time?

Upvotes: 3

Views: 2364

Answers (2)

David Heffernan
David Heffernan

Reputation: 613073

The code in your question did compile in older versions, but it should not have done. The behaviour seen in 10.2 is correct.

What happens in old versions is very strange. The compiler selects this overload in TStream:

function Write(const Buffer: TBytes; Count: Longint): Longint; overload;

That is especially egregious because what has been passed to this method is the address of the static array CFourBytes. Which is categorically not a TBytes object.

Now it just so happens that a TBytes variable is the address of the first element of the array. And nothing in the TBytes override for TMemoryStream.Write refers to Length() of that bogus TBytes object. So your code happens to work as intended. This is very clearly a compiler error that has been fixed.

Your code has always been broken, you have just, up until now, been getting away with it by fortune. You should fix your code. Like this:

LStream := TMemoryStream.Create;
try
  LStream.WriteBuffer(CFourBytes, SizeOf(CFourBytes));

  SetLength(LBuffer, LStream.Size);
  LStream.Position := 0;
  LStream.ReadBuffer(LBuffer[0], LStream.Size);
finally
  LStream.Free;
end;

Note that I am using WriteBuffer and ReadBuffer instead of Write and Read. These are the preferred methods to use with TStream. The reason being that they perform error checking and raise exceptions in case of errors, unlike Write and Read.

Upvotes: 3

MBo
MBo

Reputation: 80197

Perhaps nothing has been changed.

TStream.Write/Read methods always used untyped const/var parameter const Buffer (help) and using address of variable is wrong (because method (to be exact - compiler) finds address of variable itself).

Probably you accidentally confuse these methods with Read/WriteData ones that use typed parameter and one of overloaded versions gets Pointer type argument.

Here ReadData implementation dereferences this pointer and uses Read internally (Read in its turn calls Move and the last routine gets address of buffer again :) )

Upvotes: 1

Related Questions