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

2

5

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)

Melo answered 19/6, 2016 at 17:45 Comment(0)
Y
6

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.

Youth answered 19/6, 2016 at 19:4 Comment(2)
I was hoping to let the system manage the destruction of the WideString "object". if I use a class structure I need to manually free each object (there is no OwnsObject in D7 for TStringList). I'm still not sure if I must free strings which are allocated with SysAllocString or the system can take care of that for me?...Melo
Yes, you must free BSTR strings allocated by SysAllocString.Youth
A
4

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.

Ashely answered 19/6, 2016 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.