views:

5094

answers:

4

We had the following code previous to Delphi 2009:

function MemoryStreamToString(M: TMemoryStream): String;
var
  NewCapacity: Longint;
begin
  if (M.Size = 0) or (M.Memory = nil) then
    Result:= ''
  else
  begin
    if TMemoryStreamProtected(M).Capacity = M.Size then
    begin
      NewCapacity:= M.Size+1;
      TMemoryStreamProtected(M).Realloc(NewCapacity);
    end;
    NullString(M.Memory^)[M.Size]:= #0;
    Result:= StrPas(M.Memory);
  end;
end;

How might we convert this code to support Unicode now with Delphi 2009?

+2  A: 

I have not upgraded yet but my understanding:

NewCapacity := (M.Size + 1) * SizeOf(Char);

Loren Pechtel
+7  A: 

Or perhaps you can refactor your code to use directly a TStringStream directly? You can use it instead of TMemoryStream (they have the same interface) and you can 'convert' it to a string by simply calling myString := myStringStream.DataString;

Indeed, that was the first thing that came to mind. Why not create a TStringStream, load the memorystream in it, and return the datastring?
The_Fox
+23  A: 

The code you have is unnecessarily complex, even for older Delphi versions. Why should fetching the string version of a stream force the stream's memory to be reallocated, after all?

function MemoryStreamToString(M: TMemoryStream): string;
begin
  SetString(Result, M.Memory, M.Size div SizeOf(Char));
end;

That works in all Delphi versions, not just Delphi 2009. It works when the stream is empty without any special case. SetString is an under-appreciated function.

If the contents of your stream aren't changing to Unicode with your switch to Delphi 2009, then you should use this function instead:

function MemoryStreamToString(M: TMemoryStream): AnsiString;
begin
  SetString(Result, M.Memory, M.Size);
end;

That's equivalent to your original code, but skips the special cases.

Rob Kennedy
I've done a lot of Delphi memory stuff, but I hadn't heard about SetString, always used SetLength(dest, length) and a Move(src, @(dest[1]), length); which SetString does as well (it calls _LStrFromPCharLen)
Davy Landman
It appears that I would need to typecast M.Memory to a PChar as Pointer is not compatible with PChar. I will give it a try.
dmillam
typecasting it to a PChar should not create any problems.
Davy Landman
Pointer is compatible with everything. The only reason you'd need to type-cast is if the compiler had trouble with with overload resolution.
Rob Kennedy
Nice -- much better than my code. ;-)
Nick Hodges
Nice one Rob. I was an underappreciator of SetString.
cjrh
First function is not working with D7. The compiler fails at the second parameter ("memory") with the message "incompatible types".So, it needs indeed a pchar typecast.
Altar
+3  A: 

A "cleaner" way might be:

function StreamToString(aStream: TStream): string;
var
  SS: TStringStream;
begin
  if aStream <> nil then
  begin
    SS := TStringStream.Create('');
    try
      aStream.Position := 0;
      SS.CopyFrom(aStream, aStream.Size);
      Result := SS.DataString;
    finally
      SS.Free;
    end;
  end else
  begin
    Result := '';
  end;
end;
Nick Hodges