Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Must I free a BSTR (WideString) allocated with SysAllocString?

I have this code (I need to add string object to TStringList):

var
  WS: WideString;
begin
  WS := 'allocated string';
  SL.AddObject('my string', TObject(SysAllocString(PWideChar(WS))));

And later read it:

var
  WS: WideString;
begin
  WS := PWideChar(SL.Objects[0]);
  ShowMessage(WS);

I was wondering if the system will take care of the BSTR which was allocated with SysAllocString. or must I call SysFreeString? it's not clear from the documentation.

Now, If the system does De-allocates it, is there any way to prove it does?

P.S: Infact, it is suffucient to call:

SL.AddObject('my string', TObject(PWideChar(WS)));

Without using SysAllocString. (and I can't understand how it works)

like image 756
zig Avatar asked Jun 19 '16 17:06

zig


2 Answers

Here the following line does allocate a new BSTR and fill its pointer to the SL.Objects[] pointer.

  SL.AddObject('my string', TObject(SysAllocString(PWideChar(WS))));

So the following will definitively leak memory:

var
  WS: WideString;
begin
  WS := PWideChar(SL.Objects[0]);

Here a new WS instance will be allocated, so your BSTR instance pointed by SL.Objects[0] won't be released.

And the following is working by chance:

SL.AddObject('my string', TObject(PWideChar(WS)));

The memory pointed by the PWideChar(WS) memory buffer is still containing to the previous WS: WideString instance. So is may work... until the buffer is re-used and overriden by some other data, and another text is returned, or a random GPF occurs.

By advice: never cheat the Delphi type system, storing something else than a TObject in a variable typed as TObject... unless you know what you are doing. Don't play with pointers until you know what they are and how they work.

I do not see any benefit of storing a WideString within a TStrings.Object[] entry! Change your data structure: create a true class, storing your string. Then everything would be clear and clean:

type
  TMyStoreWS = class
  protected
    fText: WideString;
  public
    constructor Create(const aText: WideString); virtual;
    property Text: WideString read fText write fText;
  end;

constructor TMyStoreWS.Create(const aText: WideString);
begin
  inherited Create;
  fText := aText;
end;

...
SL.AddObject('my string', TMyStoreWS.Create(aText)); // clean
...
ShowMessage(SL.Objects[0].Text); // clean
SL.Objects[0].Free; // don't forget to release 

The small overhead of allocating a class instance is negligeable in regard to a BSTR string allocation, I can tell you. And your code would definitively be cleaner and easier to maintain/evolve.

like image 111
Arnaud Bouchez Avatar answered Nov 10 '22 16:11

Arnaud Bouchez


Delphi will free the WideString as soon as it goes out of scope.
Because WideString is a managed type.
However if you cast the widestring to a PWideChar, Delphi does not count that as a reference and will thus destroy the string as soon as the function exits, even though there is still a reference to it.

That's bad because now you've got a dangling pointer. This is why you need SysAllocString.

What SysAllocString does is make a copy of the string you feed in. This copy is not managed, so you'll need to destroy it yourself using SysFreeString.

like image 24
Johan Avatar answered Nov 10 '22 14:11

Johan