Текст книги "Excel. Трюки и эффекты"
Автор книги: Алексей Гладкий
Соавторы: Александр Чиртик
Жанры:
Технические науки
,сообщить о нарушении
Текущая страница: 8 (всего у книги 24 страниц) [доступный отрывок для чтения: 9 страниц]
В Windows существует ряд других системных путей, которые так или иначе могут пригодиться. Определяются они не менее просто, чем пути к системным папкам (листинг 4.14).
Листинг 4.14. Определение прочих системных путей
function GetSpecialDir(dirtype: Integer): String;
var
buffer: String;
begin
SetLength(buffer, MAX_PATH + 1);
SHGetSpecialFolderPath(0, PAnsiChar(buffer), dirtype, False);
GetSpecialDir := buffer;
end;
Здесь используется функция командной оболочки файловой системы (Windows Shell) SHGetSpecialFolderPath, ее объявление находится в модуле ShlObj. Среди параметров этой функции самыми значимыми для нас (кроме буфера длиной минимум МАХРАТН символов для помещения в него пути) являются два последних. Третий параметр функции SHGetSpecialFolderPath используется для указания того, расположение какой именно папки нас интересует. Если четвертый параметр функции SHGetSpecialFolderPath не равен False, то запрошенная папка будет создана, если до этого она не существовала.
Пример использования функции GetSpesialDir для составления списка (в элементе управления ListView) некоторых системных путей приведен в листинге 4.15. Из него вы также сможете узнать имена целочисленных констант, идентифицирующих некоторые папки.
Листинг 4.15. Использование функции GetSpecialDir
procedure TForm3.Button1Click(Sender: TObject);
var
item: TListItem;
begin
lvwPathes.Clear;
//Определение путей некоторых системных каталогов
//..Рабочий стол
item := lvwPathes.Items.Add();
item.Caption := 'Рабочий стол';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_DESKTOPDIRECTORY));
//..Избранное
item := lvwPathes.Items.Add();
item.Caption := 'Избранное';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_FAVORITES));
//..Шрифты
item := lvwPathes.Items.Add();
item.Caption := 'Шрифты';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_FONTS));
//..Мои документы
item := lvwPathes.Items.Add();
item.Caption := 'Мои документы';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_PERSONAL));
//..Последние документы
item := lvwPathes.Items.Add();
item.Caption := 'Последние документы';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_RECENT));
//..История
item := lvwPathes.Items.Add();
item.Caption := 'История';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_HISTORY));
//..Отправить
item := lvwPathes.Items.Add();
item.Caption := 'Отправить';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_SENDTO));
//..Меню Пуск
item := lvwPathes.Items.Add();
item.Caption := 'Пуск';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_STARTMENU));
//..Меню Программы
item := lvwPathes.Items.Add();
item.Caption := 'Программы';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_PROGRAMS));
//..Меню Автозагрузка
item := lvwPathes.Items.Add();
item.Caption := 'Автозагрузка';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_STARTUP));
//..Папка с шаблонами документов
item := lvwPathes.Items.Add();
item.Caption := 'Шаблоны';
item.SubItems.Insert(0, GetSpecialDir(CSIDL_TEMPLATES));
end;
Результат работы процедуры из листинга 4.14 приводится на рис. 4.3.
Рис. 4.3. Прочие системные пути Windows
В приведенной в листинге 4.15 процедуре определены не все пути, доступные с использованием функции SHGetSpecialFolderPath. Дело в том, что существует ряд виртуальных (не существующих реально на диске) папокМой компьютер, Принтеры, Сетевое окружение и т д.
Для некоторых упоминаемых в листинге 4.15 папок есть также аналогичные папки, содержимое которых доступно всем пользователям:
• CSIDL_COMMON_DESKTOPDIRECTORY – содержимое этой папки отображается на Рабочем столе всех пользователей;
• CSIDL_COMMON_DOCUMENTS – общие документы;
• CSIDL_COMMON_FAVORlTES—общие элементы папки Избранное;
• CSIDL_COMMON_PROGRAMS – общие для всех пользователей программы (пункт Программы меню Пуск);
• CSIDL_COMMON_STARTMENU – общие элементы, отображаемые вменю Пуск;
• CSIDL_COMMON_STARTUP – общие элементы меню Автозагрузка;
• CSIDL_COMMON_TEMPLATES – папка с общими для всех пользователей шаблонами документов.
Определение и установка текущей папкиПримечание
Большинство из перечисленных выше путей определяются только в системах Windows на ядре NT, но не в Windows 95/98/Ме.
Во время работы каждого приложения для него запоминается папка, которая считается текущей (для этого приложения). При грамотном управлении текущей папкой удобно использовать рассмотренные далее относительные пути.
Для определения текущей папки приложения можно воспользоваться функцией GetCurrentDir, приведенной в листинге 4.16.
Листинг 4.16. Определение текущей папки
function GetCurrentDir(): String;
var
len: Integer;
buffer: String;
begin
SetLength(buffer, MAX_PATH + 1);
len := GetCurrentDirectory(MAX_PATH, PAnsiChar(buffer));
GetCurrentDir := Copy(buffer, 1, len);
end;
Функция определения пути текущей папки основана на применении соответствующей API-функции GetCurrentDirectory. Вполне естественно, что она имеет пару – функцию для задания текущего каталога SetCurrentDirectory. Объявление этой функции:
function SetCurrentDirectory(lpPathName: PChar): BOOL; stdcall;
Функция принимает путь папки и возвращает ненулевое значение в случае успешного выполнения.
Преобразование путейРассмотрим несколько функций, которые могут пригодиться, если возникнет необходимость преобразования путей. Имеется в виду прежде всего преобразование имен файлов в формат MS-DOS и обратно. Этот вид преобразования наглядно продемонстрирован на рис. 4.4 (верхняя часть формы).
Иногда оказывается полезным представлять пути относительно какой-нибудь папки, но не относительно корневого каталога диска. Например, представьте, что вы разрабатываете приложение, документы которого, являющиеся неделимыми для пользователя, могут фактически состоять из большого количества файлов, расположенных в разных папках (Images, Movies, Embed). Сами папки расположены в том же каталоге, где и основной файл документа, или ниже по иерархии (во вложенных папках). Как добиться того, чтобы при копировании приложения со всеми нужными папками в другое место (на другой диск или компьютер, в другую папку) его по-прежнему можно было открыть, при этом рассчитывая, что в папках Images, Movies и Embed содержится не только нужная информация. Последнее говорит о том, что приложение должно «знать», какие файлы и в каких папках ему действительно необходимы. В таком случае пригодится относительный путь, который несет в себе информацию о количестве и направлении переходов из каталога, заданного в качестве корневого, для того чтобы мы смогли найти указанный в этом пути файл или папку.
Преобразование из абсолютного в относительный путь и наоборот продемонстрировано на рис. 4.4 (нижняя часть формы). При этом в качестве исходного пути берется содержимое текстового поля Исходный длинный путь, а в качестве пути папки для построения относительного пути – содержимое поля Текущая папка.
Рис. 4.4. Преобразование путей
На всякий случай нужно уточнить, что в относительном пути элемент. указывает на текущую папку (никуда переходить не надо), а элемент. означает папку, расположенную на один уровень выше (родительскую папку). Также следует уточнить, что под абсолютным путем понимается путь, корневым элементом которого является \ или <диск>: (С: , D: их д.).
Примечание
Все приведенные далее функции преобразования вы можете найти в модуле PathConvert, расположенном на диске, в папке с названием подраздела.
Преобразование длинных имен файлов в короткие и наоборот
Теперь рассмотрим реализацию преобразования путей. Сначала – преобразование между длинной и короткой формами. Выполняется это предельно просто, благо Windows API предусматривает соответствующие функции.
Преобразование длинного пути в короткий приводится в листинге 4.17.
Листинг 4.17. Преобразование пути из длинной в короткую форму
function LongPathToShort(path: String): String;
var
buffer: String;
len: Integer;
begin
SetLength(buffer, MAX_PATH);
len := GetShortPathName(PAnsiChar(path), PAnsiChar(buffer),
MAX_PATH);
SetLength(buffer, len);
LongPathToShort := buffer;
end;
Соответственно, обратное преобразование пути может выглядеть следующим образом (листинг 4.18).
Листинг 4.18. Преобразование пути из короткой в длинную форму
function ShortPathToLong(path: String): String;
var
buffer: String;
len: Integer;
begin
SetLength(buffer, MAX_PATH);
len := GetLongPathName(PAnsiChar(path), PAnsiChar(buffer),
MAX_PATH);
SetLength(buffer, len);
ShortPathToLong := buffer;
end;
При тестировании последнего листинга в Delphi 7 выяснилось, что API-функция GetLongPathName объявлена в модуле Windows. Возможно, в более старых или новых версиях Delphi это не так. Но в любом случае импортировать эту функцию из библиотеки Kernel32. dll предельно просто, достаточно поместить в модуль следующую строку:
function GetLongPathName(lpszLongPath: PChar;
lpszShortPath: PChar; cchBuffer: DWORD): DWORD;
stdcall; external kernel32 name 'GetLongPathNameA';
Преобразование абсолютного пути в относительный и наоборот
Теперь пришла очередь рассмотреть реализацию преобразований между абсолютной и относительной формами путей. Однако сначала рассмотрим небольшую, но полезную процедуру, используемую при преобразованиях. Процедура GetPathElements (листинг 4.19) формирует список строк из компонентов переданного ей пути (имен каталогов и имени целевого файла или каталога).
Листинг 4.19. Разбиение пути на составляющие
procedure GetPathElements(path: String; elements: TStrings);
var
start, pos: Integer;
begin
start := 1;
for pos := 1 to Length(path) do
if path[pos] = '' then
begin
if start <> pos then
//Выделим имя каталога
elements.Add(Copy(path, start, pos – start))
else
//Сочетание типа '\' в середине пути пропускаем
;
start := pos + 1;
end;
pos := Length(path) + 1;
if start <> pos then
//Выделим имя последнего каталога или файла
elements.Add(Copy(path, start, pos – start));
end;
После применения процедуры GetPathElements работать с компонентами пути становится очень удобно, да к тому же и упрощается код функций преобразования, так как при их написании не нужно уделять внимание правильному выделению подстрок из строки полного пути.
Функция преобразования абсолютного пути в относительный (от заданной в параметре curdir папки) приводится в листинге 4.20.
Листинг 4.20. Преобразование абсолютного пути в относительный
function AbsPathToRelative(path, curdir: String): String;
var
pathElements, curElements: TStrings;
outPath: String;
i, j: Integer;
begin
if Copy(path, 1, 2) <> Copy(curdir, 1, 2) then
begin
//Папки на разных дисках
AbsPathToRelative := path;
Exit;
end;
//Получение составляющих абсолютного и текущего пути
pathElements := TStringList.Create;
GetPathElements(path, pathElements);
curElements := TStringList.Create;
GetPathElements(curdir, curElements);
//Пропускаем одинаковые папки
i := 0;
while (i < curElements.Count) and (i < pathElements.Count)
and (CompareText(curElements[i], pathElements[i]) = 0) do Inc(i);
//Добавляем небходимое количество переходов вверх для того,
//чтобы из папки curdir попасть в общую для path и curdir папку
for j := i to curElements.Count-1 do
outPath := outPath + '..';
//Заходим из папки полученной (общей) папки в папку path
for j := i to pathElements.Count – 2 do
outPath := outPath + pathElements[j] + '';
//Последним добавляем имя конечной папки или файла
AbsPathToRelative := outPath + pathElements[pathElements.Count – 1];
//Списки строк больше не нужны
pathElements.Free;
curElements.Free;
end;
При преобразовании нужно учитывать, что пути, не принадлежащие одной иерархии (например, локальный и сетевой или пути, принадлежащие разным дискам, не могут быть представлены один относительно другого: у них нет общего родительского каталога.
Обратное преобразование относительного пути в абсолютный приведено в листинге 4.21. Здесь нужно отметить, что если путь папки curdir относительный, то в итоге получим также относительный путь (только относительно другой папки). Поэтому функция и называется RelativePathToRelative, а не RelativePathToAbs.
ПоискЛистинг 4.21. Преобразование относительного пути в абсолютный
function RelativePathToRelative(path, curdir: String): String;
var
pathElements, curElements: TStrings;
outPath: String;
i: Integer;
begin
//Получение списка составляющих абсолютного и текущего пути
pathElements := TStringList.Create;
GetPathElements(path, pathElements);
curElements := TStringList.Create;
GetPathElements(curdir, curElements);
//Изначально находимся в последней папке пути curdir
//"Путешествуем" от текущей папки вверх или вниз
//по дереву каталогов
//(прибавляя или удаляя компоненты пути в список curElements)
for i := 0 to pathElements.Count–1 do
begin
if pathElements[i] = '..' then
//Вверх по дереву
if (curElements.Count > 0)then
curElements.Delete(curElements.Count – 1)
else
curElements.Append('..')
else if pathElements[i] <> '.' then
//Вниз по дереву (знак текущей папки "." не изменяет
//положение)
curElements.Append(pathElements[i]);
end;
//Формируем результирующий путь
if (curElements.Count > 0) then outPath := curElements[0];
for i := 1 to curElements.Count-1 do
outPath := outPath + '' + curElements[i];
RelativePathToRelative := outPath;
//Списки строк больше не нужны
pathElements.Free;
curElements.Free;
end;
Поиск является неотъемлемой частью работы с файловой системой. Даже простой просмотр содержимого любого каталога сопряжен с использованием простейших, но все-таки поисковых средств (перебор и, возможно, отсеивание элементов каталога). Поэтому далее мы рассмотрим возможные варианты реализации двух удобных функций поиска: поиск по маске и атрибутам файлов в пределах заданной папки и такой же поиск по всему дереву каталогов, начиная от заданной корневой папки. Все рассмотренные далее функции поиска можно найти в модуле Search, расположенном на диске, в папке с названием подраздела.
Но сначала немного сведений о масках для поиска и атрибутах файлов (и папок).
Маски и атрибуты
Маска имени файла или папки представляет собой строку, в которой неизвестный одиночный символ можно менять на? а произвольное количество (0 и более) неизвестных заранее символов – на *. Остальные (допустимые в имени) символы обозначают сами себя. Например, имена файлов SomeFile. ехе и Some. ехе удовлетворяют каждой из масок: Some* и Some*. ехе.
Атрибуты определяют некоторые важные особенности файла. Так, например, при просмотре каталога при помощи API-функций папка может отличаться от файла только наличием атрибута FILE_ATTRIBURE_DIRECTORY. Вообще содержимое папки (директории, каталога) записано на диске в самый обычный файл. Его отличает наличие указанного неизменяемого вручную атрибута и строго заданный формат записей, а также наличие специальных функций, скрывающих от нас все особенности работы с данными каталога (открытие файла, поиск нужных записей).
Итак, далее об атрибутах. Ниже приводится перечень наиболее часто используемых атрибутов файлов и каталогов (идентификаторы целочисленных констант, объявленных в модуле Windows). Если не сказано иное, атрибут можно изменить.
• FILE_ATTRIBUTE_ARCHIVE – архивный файл или каталог (на опыте замечено, что этот атрибут появляется практически у всех файлов, находящихся на диске некоторое время);
• FILE_ATTRIBUTE_DIRECTORY – атрибут каталога (атрибут нельзя самостоятельно снять или назначить);
• FILE_ATTRIBUTE_HIDDEN – скрытый файл или каталог;
• FILE_ATTRIBUTE_NORMAL – означает отсутствие особых атрибутов у файла или каталога (у последнего, естественно, всегда установлен атрибут FILE_ ATTRIBUTE_DIRECTORY);
• FILE_ATTRIBUTE_READONLY – файл или каталог только для чтения;
• FILE_ATTRIBUTE_SYSTEM – системный файл или каталог;
• FILE_ATTRIBUTE_TEMPORARY – временный файл (файловая система стремится по возможности хранить все содержимое открытого временного файла в памяти для ускорения доступа к находящимся в нем данным).
Были рассмотрены основные атрибуты, которые могут быть присвоены объектам файловой системы (файлам и папкам), но не было сказано, как получить или установить атрибуты файла или каталога. Атрибуты можно получить при просмотре содержимого каталога (как в рассмотренных далее функциях поиска). А можно использовать для этого API-функцию GetFileAttributes. Она принимает путь файла (PChar) и возвращает значение типа DWORD (32-битное целое значение), представляющее собой битовую маску. Если функция GetFileAttributes завершается неудачно, то возвращаемое значение равно $FFFFFFFF (-1 при переводе к беззнаковому целому).
Каждому из рассмотренных атрибутов соответствует бит в возвращаемом функцией GetFileAttributes значении. Вот отрывок программы, определяющей, является ли файл системным:
var attrs: DWORD;
begin
attrs := GetFileAttribute(PAnsiChar('C:boot.ini'));
if (attrs and FILE_ATTRIBUTE_SYSTEM <> 0) then {файл системный};
Атрибуты устанавливаются при помощи API-функции SetFileAttributes. Она принимает два параметра: путь файла или папки (PChar) и битовую маску атрибутов. Возвращает 0 (False) в случае неудачи и ненулевое значение в противном случае.
Поскольку в функцию SetFileAttributes передается маска, хранящая сведения сразу обо всех атрибутах файла или папки, то изменять атрибуты нужно аккуратно (чтобы не удалить установленные ранее). Пример (отрывок программы) «включения» одного и одновременного «выключения» другого атрибута файла приведен в листинге 4.22 (проверка ошибок для простоты не производится).
Листинг 4.22. Изменение атрибутов файла
var attrs: DWORD;
begin
attrs := GetFileAttributes('C:text.txt');
attrs := attrs or FILE_ATTRIBUTE_HIDDEN; //Установка атрибута «скрытый»
attrs := attrs and not FILE_ATTRIBUTE_ARCHIVE; //Снятие атрибута «архивный»
SetFileAttributes('C:text.txt', attrs);
Поиск в указанной папке
Поиск в пределах одной папки представляет собой простой перебор всех элементов каталога с отбором тех, имена которых удовлетворяют маске и заданному набору атрибутов. В приведенном ниже примере (листинг4.23) используется API-функция FindFirstFile, которая начинает просмотр заданного каталога, автоматически отсеивая имена файлов и папок, не удовлетворяющи х маске. Функция возвращает дескриптор (THandle), используемый для идентификации начатого просмотра папки при продолжении поиска (в функции FindNextFile).
После окончания просмотра папки вызывается функция FindClose, завершающая просмотр папки. Очень напоминает работу с обычным файлом (открытие, просмотр, закрытие), не так ли?
Листинг 4.23. Поиск в заданной папке
function SearchInFolder(folder, mask: String; flags: DWORD;
names: TStrings; addpath: Boolean = False): Boolean;
var
hSearch: THandle;
FindData: WIN32_FIND_DATA;
strSearchPath: String;
bRes: Boolean; //Если равен True, то нашли хотя бы один
//файл или каталог
begin
strSearchPath := folder + '' + mask;
bRes := False;
//Начинаем поиск
hSearch := FindFirstFile(PAnsiChar(strSearchPath), FindData);
if (hSearch <> INVALID_HANDLE_VALUE) then
begin
//Ищем все похожие элементы (информация о первом элементе
//уже записана в FindData функцией FindFirstFile)
repeat
if (String(FindData.cFileName) <> '..') and
(String(FindData.cFileName) <> '.') then
//Пропускаем . и ..
begin
if MatchAttrs(flags, FindData.dwFileAttributes) then
begin
//Нашли подходящий объект
if addpath then
names.Add(folder + '' + FindData.cFileName)
else
names.Add(FindData.cFileName);
bRes := True;
end;
end;
until FindNextFile(hSearch, FindData) = False;
//Заканчиваем поиск
FindClose(hSearch);
end;
SearchInFolder := bRes;
end;
Результатом работы функции SearchlnFolder является заполнение списка names именами или, если значение параметра addpath равно True, полными путями найденных файлов и каталогов. Значение параметра flags (битовая маска атрибутов) формируется так же, как для функции SetFileAttributes. Только одновременно можно установить любые интересующие программиста атрибуты. При нахождении хотя бы одного файла или каталога SearchlnFolder возвращает значение True.
В функции поиска проверка соответствия атрибутов найденных файлов и каталогов производится при помощи дополнительной функции MatchAttrs. Код этой функции приведен в листинге 4.24.
Листинг 4.24. Фильтр атрибутов
function MatchAttrs(flags, attrs: DWORD): Boolean;
begin
MatchAttrs := (flags and attrs) = flags;
end;
Может показаться, что проверка из одной строки – слишком слабый аргумент для создания отдельной функции. В рассматриваемом примере отдельная функция MatchAttrs выделена для того, чтобы сделать отсеивание файлов (и папок) по атрибутам более очевидным.
В листинге 4.24 приводится реализация нестрогого фильтра: он принимает файл или папку, если они имеют все установленные в flags атрибуты, независимо от наличия файла или папки дополнительных атрибутов. Так, если мы задали flags:= FILE_ATTRIBUTE_READONLY, то будут найдены как файлы, так и каталоги, а также скрытые, системные и прочие файлы, также имеющие атрибут FILE_ATTRIBUTE_READONLY. Для реализации строгого фильтра можно заменить выражение в функции MatchAttrs простым равенством: flags = attrs.
Возможный результат поиска с использованием функции SearchlnFolder приводится на рис. 4.5.
Рис. 4.5. Поиск в заданной папке
Пример вызова функции SearchlnFolder (для показанного на рис. 4.5 приложения) приведен в листинге 4.25.
Листинг 4.25. Использование функции SearchlnFolder
//Запуск поиска файла в заданной папке
procedure TForm2.Button1Click(Sender: TObject);
var
flags: DWORD;
begin
//Формируем набор атрибутов (по установленным флажкам на форме)
flags := 0;
if (chkDirs.Checked) then flags := flags or FILE_ATTRIBUTE_DIRECTORY;
if (chkHidden.Checked) then flags := flags or FILE_ATTRIBUTE_HIDDEN;
if (chkSystem.Checked) then flags := flags or FILE_ATTRIBUTE_SYSTEM;
if (chkReadOnly.Checked) then flags := flags or FILE_ATTRIBUTE_READONLY;
if (chkArchive.Checked) then flags := flags or
FILE_ATTRIBUTE_ARCHIVE;
lblFound.Caption := 'Поиск…';
lstFiles.Clear;
Refresh;
//Поиск (файлы записываются прямо в список на форме)
if not SearchInFolder(txtFolder.Text, txtMask.Text, flags,
lstFiles.Items)
then
lblFound.Caption := 'Поиск не дал результатов'
else
lblFound.Caption := 'Найдено объектов: ' +
IntToStr(lstFiles.Count);
end;
Поиск по всему дереву каталогов
В листинге 4.26 приводится одна из возможных реализаций рекурсивного поиска по дереву каталогов. Алгоритм поиска работает следующим образом.
1. Выполняется поиск в папке folder (все найденные файлы или папки добавляются в список names).
2. Функция SearchlnTree вызывается для каждого подкаталога BfoLder для продолжения поиска в поддереве, определяемом подкаталогом.
Листинг 4.26. Поиск по дереву каталогов
function SearchInTree(folder, mask: String; flags: DWORD;
names: TStrings; addpath: Boolean = False): Boolean;
var
hSearch: THandle;
FindData: WIN32_FIND_DATA;
bRes: Boolean; //Если равен True, то нашли хотя бы один файл или каталог
begin
//Осуществляем поиск в текущей папке
bRes := SearchInFolder(folder, mask, flags, names, addpath);
//Продолжим поиск в каждом из подкаталогов
hSearch := FindFirstFile(PAnsiChar(folder + '*'), FindData);
if (hSearch <> INVALID_HANDLE_VALUE) then
begin
repeat
if (String(FindData.cFileName) <> '..') and
(String(FindData.cFileName) <> '.') then
//Пропускаем . и ..
begin
if (FindData.dwFileAttributes and
FILE_ATTRIBUTE_DIRECTORY <> 0)
then
//Нашли подкаталог – выполним в нем поиск
if SearchInTree(folder + '' + String(FindData.cFileName),
mask, flags, names, addpath)
then
bRes := True;
end;
until FindNextFile(hSearch, FindData) = False;
FindClose(hSearch);
end;
SearchInTree := bRes;
end;
В функции SearchlnTree не используется просмотр каталога folder вручную (при помощи API-функций) из соображений эффективности. Если захотите, можете реализовать поиск подкаталогов при помощи функции SearchlnFolder. Правда, при этом нужно будет завести дополнительный список (TStringList) для сохранения найденных в текущем каталоге подкаталогов. Элементы списка будут использоваться только один раз: для поиска в подкаталогах.
Возможный результат поиска с использованием функции SearchlnTree приводится на рис. 4.6.
Рис. 4.6. Поиск по дереву каталогов
С небольшими модификациями алгоритм рекурсивного обхода дерева каталогов, реализованный в листинге 4.25, можно использовать и при операциях, отличных от простого поиска: например, при копировании или удалении дерева каталогов. Для этого достаточно выполнять нужные операции над каждым найденным объектом.