PawelPepe 1 Posted October 31, 2023 (edited) Hi, I got a problem with TListView and Data. It seems that I got memory leaks, because Data pointers are not released (if I understand it well). Let me show you what I do: Here is a simple TListView (LV_Data). I add few items to the list (Report Style) into 3 Columns. var i : Integer; ListItem : TListItem; PObject : PChar; for i := 0 to 3 do begin // Caption ListItem := AList.Items.Add; ListItem.Caption := 'My Caption' + '_' + Inttostr(i+1); // Language Name ListItem.SubItems.Add('Language' + '_' + Inttostr(i+1)); // User Name ListItem.SubItems.Add('User Name' + '_' + Inttostr(i+1)); // User Data Path PObject := StrAlloc(250); StrPCopy(PObject, 'PATH_TO_USER_DIRECTORY' + '_' + Inttostr(i+1)); ListItem.Data := PObject; end; As you can see, I also add "hidden" data (in this example a path to directory, as string). I can use it in program, without displaying it to user in listview. I can read it in simple way (for examle, in ListView OnClick event:) var sPath : string; if (LV_Data.Items.Count > 0) AND (LV_Data.ItemIndex <> -1) then begin sPath := StrPas(PChar(LV_Data.Items[LV_Data.ItemIndex].Data)); end; Everything works very nice... but, when I close program, I see there are some memory leaks. --------------------------- Unexpected Memory Leak --------------------------- An unexpected memory leak has occurred. The unexpected small block leaks are: 473 - 520 bytes: Unknown x 3 --------------------------- OK --------------------------- As far as I know, I have to free those List View Data bymyself. How to do this? procedure TMainFrm.LV_DataDeletion(Sender: TObject; Item: TListItem); begin if Item.Data <> nil then begin FreeMem(Item.Data); Item.Data := nil; end; end; Above code not working (why!?), got Invalid Pointer Operation. Could you please tell me how to free this data to avoid memory leak? Thank you in advance, -Pawel Edited October 31, 2023 by PawelPepe Share this post Link to post
Remy Lebeau 1436 Posted October 31, 2023 (edited) As you already know, you need to use the TListView.OnDeletion event to dispose of the TListItem.Data memory that you are allocating. If you read the documentation for StrAlloc(), the correct way to free that memory is with StrDispose(), not FreeMem(): procedure TMainFrm.LV_DataDeletion(Sender: TObject; Item: TListItem); begin if Item.Data <> nil then begin StrDispose(PChar(Item.Data)); Item.Data := nil; end; end; Alternatively, a slightly easier way is to use a normal String and let the compiler manage it. A String is implemented as a pointer, and you can store that pointer in the TListItem.Data. You would just have to ensure its reference count is incremented while stored in TListItem.Data, and decremented when you are done using it: var i : Integer; ListItem : TListItem; PObject : Pointer; begin for i := 0 to 3 do begin ListItem := AList.Items.Add; ... PObject := nil; String(PObject) := '...'; ListItem.Data := PObject; end; procedure TMainFrm.LV_DataDeletion(Sender: TObject; Item: TListItem); var PObject: Pointer: begin if Item.Data <> nil then begin PObject := Item.Data; Item.Data := nil; String(PObject) := ''; end; end; var sPath : string; begin if (LV_Data.Items.Count > 0) AND (LV_Data.ItemIndex <> -1) then begin sPath := String(LV_Data.Items[LV_Data.ItemIndex].Data); end; That being said, a much safer and cleaner solution is to not use the TListItem.Data property at all. Instead, derive a new class from TListItem and add a String member to it, then use the TListView.OnCreateItemClass event to let TListView create instances of your derived class: type TMyListItem = class(TListItem) public Path: string; end; procedure TMainFrm.LV_DataCreateItemClass(Sender: TCustomListView; var ItemClass: TListItemClass); begin ItemClass := TMyListItem; end; var i : Integer; ListItem : TListItem; begin for i := 0 to 3 do begin ListItem := AList.Items.Add; ... TMyListItem(ListItem).Path := '...'; end; var sPath : string; begin if (LV_Data.Items.Count > 0) AND (LV_Data.ItemIndex <> -1) then begin sPath := TMyListItem(LV_Data.Items[LV_Data.ItemIndex]).Path; end; No mess, no fuss. The memory is managed for you, and will be cleaned up automatically, no OnDeletion handler needed. Edited November 1, 2023 by Remy Lebeau 1 Share this post Link to post
PawelPepe 1 Posted October 31, 2023 Remy Lebeau Thank you!!! It is working (first method) I will also try the second one. Thank you for very fast response and your knowledge. Best regards, -Pawel Share this post Link to post
Remy Lebeau 1436 Posted October 31, 2023 I updated my answer with a 3rd option, too 1 Share this post Link to post