shawn857
asked on
Memory leak using records in a TVirtualStringTree
Dear Experts, I'm having a difficult time finding a memory leak caused by the introduction of a TVirtualStringTree into my application. It doesn't help that using a TVirtualStringTree is confusing to me to begin with. I *think* I know what might be causing it, but don't know how/where exactly in my code to free the variables used. To boil things down to their simplest, I'm reading in some lines of text from a file, then for each line of text, calling a function which adds the whole line to Column 0 of the TVirtualStringTree:
My function:
My GetText event is like so:
And the declaration of my data structures look like this:
My thinking is that the creation (and not disposing of) of the local variable "Data: PTreedata;" in function AddWholeRecord is the culprit. I tried adding the line "Dispose(Data)" at the end of that function but it gave a massive runtime error. I think the GetText event is still using the info in variable "Data" well after the AddWholeRecord function completes. At least this is what is seems like as I step through things in debug. To me, I just can't follow when that GetText event gets fired and how many times it runs - it seems to run multiple times for each piece of data and it just doesn't make sense to me. Anyway, would anyone be able to shed some light on where this memory leak might be cropping up and what I'm not disposing of correctly?
Thanks
Shawn
P.S: I'm using Delphi 7
AddWholeRecord(VirtualStringTree1, nil, Inrec);
My function:
function TForm5.AddWholeRecord(aVirtualStringTree: TCustomVirtualStringTree; ANode: PVirtualNode;
hinrec : string): PVirtualNode;
var
Data: PTreeData;
i: integer;
AVirtualTreeColumn: TVirtualTreeColumn;
begin
WHILE VIRTUALSTRINGTREE1.HEADER.COLUMNS.COUNT < 1 DO
BEGIN
AVirtualTreeColumn:=VirtualStringTree1.Header.Columns.Add;
AVirtualTreeColumn.Text:='';
AVirtualTreeColumn.Options:=AVirtualTreeColumn.Options-[coAllowClick];
END;
Result := aVirtualStringTree.AddChild(ANode);
Data := aVirtualStringTree.GetNodeData(Result);
aVirtualStringTree.ValidateNode(Result, False);
SetLength(Data^.DataString, 1);
Data^.DataString[0]:=hinrec;
end; // AddWholeRecord
My GetText event is like so:
procedure TForm5.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: WideString);
Var
hData: PTreeData;
hval : integer;
begin
hData := VirtualStringTree1.GetNodeData(node);
hval:= High(hData^.DataString);
if Column <= hval then
cellText:=hData^.DataString[Column]
else
cellText:='';
end; // VirtualStringTree1GetText
And the declaration of my data structures look like this:
type
PTreeData = ^TTreeData;
TTreeData = record
DataString: array of string;
end;
My thinking is that the creation (and not disposing of) of the local variable "Data: PTreedata;" in function AddWholeRecord is the culprit. I tried adding the line "Dispose(Data)" at the end of that function but it gave a massive runtime error. I think the GetText event is still using the info in variable "Data" well after the AddWholeRecord function completes. At least this is what is seems like as I step through things in debug. To me, I just can't follow when that GetText event gets fired and how many times it runs - it seems to run multiple times for each piece of data and it just doesn't make sense to me. Anyway, would anyone be able to shed some light on where this memory leak might be cropping up and what I'm not disposing of correctly?
Thanks
Shawn
P.S: I'm using Delphi 7
ASKER
Thanks Merijn, but that OnFreeNode event met with a catastrophic number of "RunTime Error 216" messages. Several - one after another non-stop. Your code had a couple of compile errors so I had to modify to the best of my knowledge to make it compile. Here are the two routines now (AddWholeRecord and OnFreeNode):
In the FreeNode event, I added this var declaration:
aVirtualStringTree: TCustomVirtualStringTree;
Maybe that's not correct, I don't know. Also, this line wouldn't compile:
SetLength(Data^).DataStrin g, 0);
So I modified it to : SetLength(Data^.DataString , 0);
Thanks
Shawn
function TForm5.AddWholeRecord(aVirtualStringTree: TCustomVirtualStringTree; ANode: PVirtualNode;
hinrec : string): PVirtualNode;
var
Data: PTreeData;
i: integer;
AVirtualTreeColumn: TVirtualTreeColumn;
begin
WHILE VIRTUALSTRINGTREE1.HEADER.COLUMNS.COUNT < 1 DO
BEGIN
AVirtualTreeColumn:=VirtualStringTree1.Header.Columns.Add;
AVirtualTreeColumn.Text:='';
AVirtualTreeColumn.Options:=AVirtualTreeColumn.Options-[coAllowClick];
END;
Result := aVirtualStringTree.AddChild(ANode);
New(Data);
Data := aVirtualStringTree.GetNodeData(Result);
aVirtualStringTree.ValidateNode(Result, False);
SetLength(Data^.DataString, 1);
Data^.DataString[0]:=hinrec;
end; // AddWholeRecord
procedure TForm5.VirtualStringTree1FreeNode(Sender: TBaseVirtualTree;
Node: PVirtualNode);
var
Data: PTreeData;
aVirtualStringTree: TCustomVirtualStringTree;
begin
Data := PTreeData(aVirtualStringTree.GetNodeData(Node)^);
SetLength(Data^.DataString, 0);
Dispose(Data);
end;
In the FreeNode event, I added this var declaration:
aVirtualStringTree: TCustomVirtualStringTree;
Maybe that's not correct, I don't know. Also, this line wouldn't compile:
SetLength(Data^).DataStrin
So I modified it to : SetLength(Data^.DataString
Thanks
Shawn
Sorry, misread your code. In OnFreeNode it should be VirtualStringTree1, not aVirtualStringTree. You can remove the variable you added.
ASKER
Thanks Merijn, but now get the attached error when I close down my app.
After clicking OK on that, appears this:
-------------------------- -
Application Error
-------------------------- -
Exception EInvalidPointer in module MyApp.exe at 00002AD8.
Invalid pointer operation.
-------------------------- -
OK
-------------------------- -
Is this line correct:
Data := PTreeData(VirtualStringTre e1.GetNode Data(Node) ^);
Should that ^ be after (Node) ?
Thanks
Shawn
FreeNode.JPG
After clicking OK on that, appears this:
--------------------------
Application Error
--------------------------
Exception EInvalidPointer in module MyApp.exe at 00002AD8.
Invalid pointer operation.
--------------------------
OK
--------------------------
Is this line correct:
Data := PTreeData(VirtualStringTre
Should that ^ be after (Node) ?
Thanks
Shawn
FreeNode.JPG
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks Merijn, but I'm a little confused still on what I should have in OnFreeNode. I have this currently:
... because a couple of posts ago you told me this - "In OnFreeNode it should be VirtualStringTree1, not aVirtualStringTree". But in your latest code, you have:
Also, I used your recommended change in GetText and it gives a runtime error right away before it gets to display the records:
-------------------------- -
MyApp
-------------------------- -
Access violation at address 00405966 in module 'MyApp.exe'. Read of address 73726942.
-------------------------- -
OK
-------------------------- -
I changed my existing line:
...to this that you recommended:
Putting back in my previous line of code makes the text display fine in the TVirtualStringTree... but the memory leaks that last occurred are back after program closure :-(
Thanks
Shawn
Data := PTreeData(VirtualStringTree1.GetNodeData(Node)^);
... because a couple of posts ago you told me this - "In OnFreeNode it should be VirtualStringTree1, not aVirtualStringTree". But in your latest code, you have:
Data := PTreeData(aVirtualStringTree.GetNodeData(Node)^);
Also, I used your recommended change in GetText and it gives a runtime error right away before it gets to display the records:
--------------------------
MyApp
--------------------------
Access violation at address 00405966 in module 'MyApp.exe'. Read of address 73726942.
--------------------------
OK
--------------------------
I changed my existing line:
hData := VirtualStringTree1.GetNodeData(node);
...to this that you recommended:
hData := PTreeData(VirtualStringTree1.GetNodeData(node)^);
Putting back in my previous line of code makes the text display fine in the TVirtualStringTree... but the memory leaks that last occurred are back after program closure :-(
Thanks
Shawn
ASKER
Merijn, I think I've got it... found a discrepancy between how I was doing things compared to your full source code. Think I have it straightened out now but still doing some testing. Will let you know soon!
Thanks!
Shawn
Thanks!
Shawn
ASKER
Merijn, it looks like I have this task working now... but I've discovered another memory leak unrelated to the TVirtualStringTree. I'm going to post a new question about that in a few minutes. I'd like to get that new problem solved and make sure everything is working before I wrap up this particular question.
Thanks
Shawn
Thanks
Shawn
ASKER
Sorry, not a memory leak - but an "Access Violation" when trying to "free" an object....
Shawn
Shawn
ASKER
I solved my problem with the "Access Violation" Merijn - used "FreeAndNil" instead of ".Free" and it fixed things... so no need to post another question. And your code suggestions for this question worked perfectly... everything is fine now! Thank you so much!
Cheers
Shawn
Cheers
Shawn
ASKER
Excellent... thank you!
Open in new window
Then make an event handler for OnFreeNode, and do something like:
Open in new window
(caution, not actually tested this, but this is more or less a 1:1 copy of my working code.