-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuShellUtils.pas
More file actions
152 lines (134 loc) · 4.25 KB
/
Copy pathuShellUtils.pas
File metadata and controls
152 lines (134 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
unit uShellUtils;
{
File-system / shell helpers used to fill the VirtualTreeView controls:
- the Windows system small-icon image list (for real shell icons),
- per-file icon index + type name via SHGetFileInfo,
- directory enumeration (folders first, then files).
Nothing here depends on VirtualTrees, so it can be unit-tested in isolation.
}
interface
uses
Winapi.Windows, System.SysUtils, System.Classes, Vcl.Controls,
System.Generics.Collections;
type
TFileEntry = record
DisplayName: string;
FullPath: string;
IsDir: Boolean;
Size: Int64;
Modified: TDateTime;
IconIndex: Integer;
TypeName: string;
end;
{ Creates a TImageList that shares the Windows system small-icon image list.
ShareImages is set so freeing the TImageList will not destroy the system list. }
function CreateSystemImageList(AOwner: TComponent): TImageList;
{ Resolves the small-icon index (into the system image list) and the friendly
type name for a file/folder name, using attributes only (no disk access). }
procedure GetShellInfo(const AName: string; AIsDir: Boolean;
out AIconIndex: Integer; out ATypeName: string);
{ Returns the entries directly inside ADir, folders first then files (each group
sorted by name). When AIncludeFiles is False only sub-folders are returned. }
function ListFolder(const ADir: string; AIncludeFiles: Boolean): TArray<TFileEntry>;
{ True if ADir contains at least one sub-folder (used to decide whether a tree
node should show an expand button). }
function HasSubfolders(const ADir: string): Boolean;
implementation
uses
Winapi.ShellAPI, System.Generics.Defaults, System.IOUtils;
function CreateSystemImageList(AOwner: TComponent): TImageList;
var
Sfi: TSHFileInfo;
H: THandle;
begin
Result := TImageList.Create(AOwner);
Result.ShareImages := True; // do not free the shared system list
FillChar(Sfi, SizeOf(Sfi), 0);
H := THandle(SHGetFileInfo('C:\', 0, Sfi, SizeOf(Sfi),
SHGFI_SYSICONINDEX or SHGFI_SMALLICON));
if H <> 0 then
Result.Handle := H;
end;
procedure GetShellInfo(const AName: string; AIsDir: Boolean;
out AIconIndex: Integer; out ATypeName: string);
var
Sfi: TSHFileInfo;
Attr: DWORD;
begin
if AIsDir then
Attr := FILE_ATTRIBUTE_DIRECTORY
else
Attr := FILE_ATTRIBUTE_NORMAL;
FillChar(Sfi, SizeOf(Sfi), 0);
SHGetFileInfo(PChar(AName), Attr, Sfi, SizeOf(Sfi),
SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or
SHGFI_TYPENAME);
AIconIndex := Sfi.iIcon;
ATypeName := Sfi.szTypeName;
end;
function HasSubfolders(const ADir: string): Boolean;
var
Sr: TSearchRec;
begin
Result := False;
if FindFirst(IncludeTrailingPathDelimiter(ADir) + '*', faDirectory, Sr) = 0 then
try
repeat
if ((Sr.Attr and faDirectory) <> 0) and (Sr.Name <> '.') and
(Sr.Name <> '..') then
Exit(True);
until FindNext(Sr) <> 0;
finally
FindClose(Sr);
end;
end;
function ListFolder(const ADir: string; AIncludeFiles: Boolean): TArray<TFileEntry>;
var
Sr: TSearchRec;
List: TList<TFileEntry>;
E: TFileEntry;
IsDir: Boolean;
Base: string;
begin
List := TList<TFileEntry>.Create;
try
Base := IncludeTrailingPathDelimiter(ADir);
if FindFirst(Base + '*', faAnyFile, Sr) = 0 then
try
repeat
if (Sr.Name = '.') or (Sr.Name = '..') then
Continue;
IsDir := (Sr.Attr and faDirectory) <> 0;
if (not IsDir) and (not AIncludeFiles) then
Continue;
E.DisplayName := Sr.Name;
E.FullPath := Base + Sr.Name;
E.IsDir := IsDir;
if IsDir then
E.Size := 0
else
E.Size := Sr.Size;
E.Modified := Sr.TimeStamp;
GetShellInfo(Sr.Name, IsDir, E.IconIndex, E.TypeName);
List.Add(E);
until FindNext(Sr) <> 0;
finally
FindClose(Sr);
end;
// Folders first, then files; each group alphabetical (case-insensitive).
List.Sort(TComparer<TFileEntry>.Construct(
function(const A, B: TFileEntry): Integer
begin
if A.IsDir <> B.IsDir then
begin
if A.IsDir then Result := -1 else Result := 1;
end
else
Result := AnsiCompareText(A.DisplayName, B.DisplayName);
end));
Result := List.ToArray;
finally
List.Free;
end;
end;
end.