Recent

Author Topic: [SOLVED] TShellTreeView crashes when deleting folders outside application.  (Read 1172 times)

Mig.BR

  • New Member
  • *
  • Posts: 12
I'm having problems with TShellTreeView. If I select a folder with subfolders and delete one of these subfolders outside the application (e.g. on Windows Explorer), the application crashes and closes.
Has anyone else had this problem and found a solution?
I even tested the solution described at: https://forum.lazarus.freepascal.org/index.php/topic,58161.msg433345.html but was not successful. It keeps crashing and closing!

To reproduce the problem on Windows and Lazarus 2.2.2, just put a TShellTreeView on a Form without any additional code.
Create a folder structure as in the image attached.
Select the MainTestFolder in the STV, as in the image, with subfolders expanded.
Open Windows Explorer and delete one of the subfolders (e.g. TestFolder03)
When you go back to the application it automatically crashes and closes.
In the error capture it returns Access Violation.
« Last Edit: July 28, 2022, 01:40:24 am by Mig.BR »

Ally

  • New Member
  • *
  • Posts: 44
I can confirm the error.
It can be avoided with XTree.UseBuiltinIcons := False;.
But then no more icons are displayed.

The error occurs when trying to draw the icon of the no longer existing folder.
A "dirty" patch prevents the error, but still shows the no longer present folder.

win32wsshellctrls.pp
Code: Pascal  [Select][+][-]
  1. { TWin32WSCustomShellTreeView }
  2.  
  3. class function TWin32WSCustomShellTreeView.DrawBuiltInIcon(ATreeView: TCustomShellTreeView;
  4.   ANode: TTreeNode; ARect: TRect): TSize;
  5. var
  6.   filename: WideString;
  7.   ico: TIcon;
  8. begin
  9.   fileName := ATreeView.GetPathFromNode(ANode);
  10.  
  11. //#############################################
  12.   if not DirectoryExists(fileName) then
  13.     Exit;
  14. //#############################################
  15.  
  16.   ico := GetShellIcon(fileName);
  17.   try
  18.     ATreeView.Canvas.Draw(ARect.Left, (ARect.Top + ARect.Bottom - ico.Height) div 2, ico);
  19.     Result := Types.Size(ico.Width, ico.Height);
  20.   finally
  21.     ico.Free;
  22.   end;
  23. end;

wp

  • Hero Member
  • *****
  • Posts: 10298
The correct solution should track the OS and monitor the contents of the tree/listview - but this should be the task of another component. Of course your idea is better than a crash, and I committed it after some modification (I checked whether ico is nil because your solution fails when a file is deleted (in case of otNonFolders in Options). And since DrawBuiltInIcon is a function I made it return a zero-size in case of this error).

Ally

  • New Member
  • *
  • Posts: 44
Hello wp,

thank you very much for taking over.
I would still have one suggestion.

Instead of:
Code: Pascal  [Select][+][-]
  1.   if ico = nil then
  2.   begin
  3.     Result := Types.Size(0, 0);
  4.     exit;
  5.   end;

works also:
Code: Pascal  [Select][+][-]
  1.   if ico = nil then
  2.     Exit(Types.Size(0, 0));

wp

  • Hero Member
  • *****
  • Posts: 10298
I know... But being an old-fashioned programmer, the longer version is easier to read for me.

Mig.BR

  • New Member
  • *
  • Posts: 12
Thank's Ally and wp for the quick help and solution to the problem. I made a small change to the proposed solution as I thought it would be interesting to keep the alignment of the name of the deleted folders (aesthetics only). :)

Code: Pascal  [Select][+][-]
  1. var
  2.   filename: WideString;
  3.   ico: TIcon;
  4.   size: TSize;
  5.  
  6.   ...
  7.  
  8.   if ico = nil then
  9.   begin
  10.     size := GetBuiltinIconSize;
  11.     Result := Types.Size(size.Width, size.Height);
  12.     exit;
  13.   end;
  14.  

Ally

  • New Member
  • *
  • Posts: 44
Here some more code aesthetics  ;) :)
Code: Pascal  [Select][+][-]
  1.   if ico = nil then
  2.     Exit(GetBuiltinIconSize);
  3.  

Mig.BR

  • New Member
  • *
  • Posts: 12
Here some more code aesthetics  ;) :)
Code: Pascal  [Select][+][-]
  1.   if ico = nil then
  2.     Exit(GetBuiltinIconSize);
  3.  

Yes, Ally, it looks better that way. I used to do it this way in Java. I'm also an old school programmer like wp. With time we value easy code interpretation, that is, we simplify. I avoid returns in Exits because in the past I had problems with memory leaks. It is not the case here but the hurry sometimes makes us inattentive to what we can or cannot return in Exits. Someone in the future may want to turn a Record into a Class. The compiler doesn't always warn us about this.

wp

  • Hero Member
  • *****
  • Posts: 10298
I think the unindented name better indicates that something is wrong with this file

 

TinyPortal © 2005-2018