-
Notifications
You must be signed in to change notification settings - Fork 273
Description
Environment:
Windows 10 Pro x64 22H2 19045.3324
Delphi 10.4.2 CE and Delphi 11.3 CE
Virtual TreeView 7.6.5a (but a much older version does the same)
Project:
Advanced Demo - Paint tree as directory tree (DrawTreeDemo.pas).
Issue:
Stack overflow exception.
The stack is flooded with InitChildren and GetChildCount calls.
Occurs in x32 and x64 builds.
Reproduction:
- Run the Advanced Demo.
- Select the 'Paint tree as directory tree' demo.
- Browse to any folder that has no subfolders, at least one file, and no supported picture files.
Just make C:\TEST, and put a text file in it for testing purposes.
Analysis:
Nodes without subfolders, with at least one file, but no supported picture files, are presented with the option to expand.
The node's properties have:
Node.ChildCount = 0
Node.States includes vsHasChildren
Trying to expand that node, VTV checks for vsHasChildren state, and calls InitChildren if needed.
InitChildren uses Sender.ChildCount[Node] to get the ChildCount it just added.
The Sender.ChildCount getter however also checks for the vsHasChildren state, and also calls InitChildren if needed.
This makes an endless loop of InitChildren and GetChildCount, which results in the fatal exception.
Nodes without subfolders, but with at least one supported picture file in it (it passes the CanDisplay check), report:
Node.ChildCount > 0
Node.States includes vsHasChildren
This make the demo continue to work for those folders.
Solution:
Using a local node counter in VDT1InitChildren (DrawTreeDemo.pas):
Introduce a local cardinal var LChildCount.
Increase it by 1 whenever AddChild is called.
Set ChildCount to LChildCount (instead of using Sender.ChildCount[Node]).
Note:
I started by changing VDT1InitNode (DrawTreeDemo.pas), so it does not add the vsHasChildren state flag to nodes that have no subfolders. Changing:
Data.Attributes := ReadAttributes(Data.FullPath);
if ((Data.Attributes and SFGAO_HASSUBFOLDER) <> 0) or
(((Data.Attributes and SFGAO_FOLDER) <> 0) and HasChildren(Data.FullPath)) then
Include(InitialStates, ivsHasChildren);
To:
Data.Attributes := ReadAttributes(Data.FullPath);
if ((Data.Attributes and SFGAO_HASSUBFOLDER) <> 0) then
Include(InitialStates, ivsHasChildren);
This fixes the crash, and is generally preferable in a directory tree structure, but it also breaks the demo.
The demo actually relies on the option to expand a node without subfolders (thus relying on the vsHasChildren state), so it can call InitChildren + InitNode to get and show pictures in the tree itself.
The demo could be changed to show files (pictures) in a separate treeview on the right, but that's beyond the scope.