I have a program which uses TListView
to visualize and store some data. TListitem
's data property is filled with a pointer to a record like so:
type
TWatch = record
name : string;
path : string;
//...
end;
procedure TfrmProcessWatcherMain.AddWatchToListView(AWatch: TWatch);
var
ANewWatch : TListItem;
begin
ANewWatch := lvWatches.Items.Add; //lvWatches is TListview
//...
ANewWatch.Data:= @AWatch;
end;
When I'm trying to retrieve this data somehow I'm getting access violation error, which is a total surprise for me because all seems legit, here is code of retrieval:
if(lvWatches.Selected <> nil) then begin
tempWatch := TWatch(lvWatches.Selected.Data^); // AV here
ShowMessage(tempWatch.name);
Also AWatch
which is passed to a first function is stored in a
WatchList : TList<TWatch>;
so it is accessible using other methods
The problem is that @AWatch
is the address of a local variable. As soon as AddWatchToListView
returns AWatch
goes out of scope and that address is no longer valid.
Instead of taking the address of a local variable you need to allocate a record on the heap using New
.
procedure TfrmProcessWatcherMain.AddWatchToListView(AWatch: TWatch);
var
ANewWatch : TListItem;
P : ^TWatch;
begin
ANewWatch := lvWatches.Items.Add;
New(P);
P^ := AWatch;
ANewWatch.Data:= P;
end;
You will need to deallocate the memory with Dispose
whenever a list item is destroyed. Do that using the list view's OnDeletion
event.
Alternatively, you could store the index of the item in WatchList
. Or the address of the record in WatchList
, which you get like this: @WatchList.List[Index]
. Both of these options rely on WatchList
not being modified after references to items are taken, which may be too constraining for you.