Search code examples
delphimemory-managementdelphi-7

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)


Solution

  • 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.