Интерфейс IShellFolder
Этот интерфейс соответствует папке — одному из основных элементов пространства имен Проводника. Зачем было вводить термин "папка", когда существовали уже общепринятые "каталог" и "директория"? В отличие от последних двух, папка может быть не просто обычным элементом файловой системы. Она может быть виртуальной — как папки Принтеры, Документы или Панель управления. Любая папка может содержать коллекцию объектов из состава пространства имен.
Получив указатель на интерфейс ishellFoider, соответствующий папке, вы можете работать с ней, как с объектом СОМ. "Верхушкой" (корневой папкой) пространства имен является папка Рабочий стол (Desktop). Получить интерфейс isheiiFoider этой папки можно путем вызова функции:
function SHGetDesktopFolder(var ppshf: IShellFolder): HResult;
Логика работы с описываемым интерфейсом такова: сначала необходимо получить интерфейс нужной папки, а затем можно переходить к работе с ее содержимым. Содержимое представляет собой список, а каждый элемент папки представлен структурой pitemiDList. Эта структура не типизирована; ее единственное обязательное поле содержит длину в байтах, зная которую можно переместиться к следующему элементу. То есть получается обычная цепочка. Все остальные поля заполняются соответствующими функциями и методами интерфейса ishellFoider.
Примечание
Все служебные функции работы со структурами PitemiDList — создание, уничтожение, копирование, перемещение по цепочке и т. п. — содержатся в примере Virtual ListView, поставляемом с Delphi. Если вы намерены писать программы, работающие с ishellFoider, целесообразно взять их на заметку. В дальнейшем для простоты эти структуры будем именовать pidl.
Рассмотрим функции интерфейса ishellFoider. Под "текущей папкой" в табл. 31.1 понимается та папка, которая в данный момент представляет интерфейс IShellFolder.
Таблица 31.1. Функции интерфейса IShellFolder
Метод |
Описание |
function ParseDisplayName (hwndOwner : HWND; pbcReserved: Pointer; IpszDisplayName: POLESTR; out pchEaten: ULONG; out ppidl: PitemiDList; var dwAttributes : ULONG) : HResult; |
Эта функция позволяет получить указатель на элемент ppidl, зная только его полное имя (с путем) IpszDisplayName |
function EnumObjects (hwndOwner: HWND; grf Flags: DWORD; out EnumlDList: lEnumlDList) : HResult; |
Возвращает указатель на специальный интерфейс lEnumlDList, предназначенный для организации цикла по всем элементам списка в текущей папке |
function BindToObject (pidl: PitemiDList; pbcReserved: Pointer; const riid: TIID; out ppvOut: Pointer) : HResult; |
Возвращает интерфейс папки pidl, которая должна находиться в текущей папке (на которую ссылается интерфейс, вызвавший этот метод) |
function ComparelDs (IParam: LPARAM; pidll, pid!2: PitemiDList): HResult; |
Сравнивает два первых элемента В списках pidll И pidl2 |
function CreateViewObject (hwndOwner: HWND; const riid: TIID; out ppvOut: Pointer) : HResult; |
Создает визуальный объект для текущей папки и возвращает указатель на него в параметре ppvOut |
function GetAttributesOf (cidl: UINT; var apidl: PItemlDList; var rgflnOut: UINT): HResult; |
Возвращает атрибуты элемента под номером cidl в списке apidl. Результат — набор флагов, устанавливаемых в параметре rgf inOut |
function GetUIObjectOf (hwndOwner : HWND; cidl: UINT; var apidl: PItemlDList; const riid: TIID; prgflnOut: Pointer; out ppvOut: Pointer) : HResult; |
Создает объект пользовательского интерфейса, связанный с элементом списка aplidl под номером cidl |
function GetDisplayNameOf (pidl: PItemlDList; uFlags: DWORD; var IpName: TStrRet) : HResult; |
Возвращает имя элемента pidl. Полнота возвращаемой информации определяется параметром uFlags |
function SetNaraeOf (hwndOwner: HWND; pidl: PItemlDList; IpszName: POLEStr; uFlags: DWORD; var ppidlOut: PItemlDList) : HResult; |
Задает новое имя IpszName для списка pidl. При этом возвращается новый указатель на список — ppidlOut |
Два метода — ParseDisplayName и GetDisplayNameOf — взаимно дополняют друг друга. Первый из них нужен, если вы имеете указатель на ishellFoider и хотите связать его с конкретной папкой. На практике это сводится к задаче в три действия:
1. Получить указатель на интерфейс какой-либо папки, скажем, рабочего стола при помощи ShGetDesktopFolder.
2. Получить указатель (pidl) нужного вам элемента. Это осуществимо многими способами. Первый из них — как раз через вызов метода IShellFolder. ParseDisplayName. Если вы хотите получить доступ к одной из виртуальных (специальных) папок, то незаменимой будет следующая функция:
function SHGetSpecialFolderLocation(hwndOwner: HWND; nFolder: Integer; var ppidl: PItemlDList): HResult;
В параметре nFolder вы задаете константу, соответствующую выбранной специальной папке. На выходе будет указатель на элемент ppidl, соответствующий этой папке.
Примечание
Во многих функциях Shell API и методах его интерфейсов встречается параметр hwndOwner. Он должен задавать дескриптор окна на тот случай, если придется выводить диалоговое окно или окно с сообщением об ошибке.
Возможные значения параметра nFolder перечислены в табл. 31.2. В комментариях к ним "виртуальная" папка является особым объектом, который предоставляется пользователю при помощи Shell API. Просто "папка" реально существует где-то в файловой системе.
Таблица 31.2. Константы, определяющие специальные папки
Значение |
Комментарий |
CSIDL_BITBUCKET |
Корзина (Recycle bin) — специальная папка для удаленных файлов. Пути к Recycle bin нет в системном реестре во избежание перемещения или удаления, и его не узнать иным методом |
CSIDL_CONTROLS |
Панель инструментов (Control Panel) — виртуальная папка, содержащая значки апплетов Панели инструментов |
CSIDL_DESKTOP |
Виртуальная папка Рабочий стол (Desktop), корневая в пространстве имен |
CSIDL_DESKTOPDIRECTORY |
Папка файловой системы, реально содержащая объекты рабочего стола |
CSIDL DRIVES |
Виртуальная папка Мой компьютер (My Computer), содержащая элементы для всех накопителей на компьютере подключенных сетевых устройств, папки Принтеры, Панель инструментов, Удаленный доступ к сети |
CSIDL FONTS |
Виртуальная папка Шрифты |
CSIDL NETHOOD |
Папка, содержащая объекты сетевого окружения |
CSIDL_NETWORK |
Виртуальная папка Сетевое окружение (Network Neighborhood) |
CSIDL_PERSONAL |
Папка Мои документы |
CSIDL_PRINTERS |
Виртуальная папка Принтеры (Printers) |
CSIDL_PROGRAMS |
Папка Программы из главного меню, содержащая папки установленных на компьютере программ |
CSIDL RECENT |
Папка, содержащая ссылки на последние использовавшиеся документы (Recent) |
CSIDL SENDTO |
Папка, содержащая элементы контекстного меню Send To... |
CSIDL_STARTMENU |
Папка, содержащая элементы главного меню Пуск (Start) |
CSIDL_STARTUP |
Папка, содержащая элементы меню Автозапуск (Startup) |
CSIDL_TEMPIATES |
Папка, содержащая шаблоны типовых документов |
Третий вариант получить pidi нужной папки — интерактивный, с помощью функции Shell API.
function ShBrowseForFolder(var Ipbi: TBrowselnfo): PItemlDList;
Перед ее вызовом следует заполнить структуру типа TBrowselnfo, содержащую в частности pidi того элемента, который будет корневым. После вызова функции пользователь увидит перед собой диалоговое окно выбора папки (рис. 31.3).
Рис. 31.3. Диалоговое окно выбора папки, созданное при вызове функции ShBrowseForFolder
В данном примере корневой служит виртуальная папка My Computer. Пользователю предоставляется возможность выбрать одну из папок файловой системы (за это отвечает флаг TBrowseinfo.uiFlags, равный
BIF_RETURNONLYFSDIRS).
На выходе функция возвращает pidi папки, имя которой извлекается из него вызовом еще одной функции Shell — shGetPathFromList.
procedure TForml/ButtonlClick(Sender: TObject) ;
var
BI : TBrowselnfo;
Image : integer;
StartPIDL, ResPIDL : PItemlDList;
S, Path : ArraytO..max_path-l] Of WideChar;
begin
01eCheck(SHGetSpecialFolderLocation(Handle, CSIDL_DRIVES, StartPIDL));
With BI do
Begin
hwndOwner = Application.Handle;
pszDisplayName = @S;
IpszTitle = 'Выберите необходимую папку';
ulFlags = BIF_RETURNONLYFSDIRS;
pidlRoot = StartPIDL;
Ipfn = nil;
iImage = 1;
end;
ResPIDL := SHBrowseForFolder(BI) ;
if SHGETPathFromlDList(ResPIDL, @Path[0])
then Labe11.Caption := StrPas(@Path[0]) ;
end;
Полученное имя здесь отображается при помощи компонента Label 1.
3. Наконец, перейдем к третьему действию нашей задачи. Теперь, зная pidi папки, с которой вы будете работать, можно получить указатель на интерфейс ishellFolder вызовом метода BindToObject. Мы еще не рассмотрели такой важный аспект работы с папками, как просмотр их содержимого. Верные правилу СОМ: "каждый должен заниматься своим делом", разработчики Shell предоставили для просмотра еще один интерфейс — IEnumiDList. Пугаться нечего, набор возможностей этого интерфейса даже меньше, чем у пульта ДУ в магнитофоне. Его четыре метода — Next, Skip, Reset и clone — позволяют организовать просмотр списка в одном направлении, а также возврат к началу и дублирование (Clone) выбранного элемента списка. Вот как это выглядит на практике.
Memol.Clear; try
01eCheck(SHGetDesktopFolder(DeskTop));
if not Succeeded(DeskTop.ParseDisplayName
(Self.Handle,nil, StringToWideChar (Editl.Text,ws, MAX_PATH),n, pidi, attr))
then begin ShowMessage('Неизвестное имя');
Exit; end; OleCheck(DeskTop.BindToObject(pidl,nil, IID_IShellFolder, Pointer(NewShellFolder)});
OleCheck(NewShellFolder.EnumObj ects{Self.Handle,
SHCONTF_FOLDERS or SHCONTF_NONFOLDERS, Enumerator)); while Enumerator.Next(1, pidl, Numpidls) = S_OK do
begin
NewShellFolder.GetDisplayNameOf(PIDL, SHGDN_FORPARSING, StrRet); case StrRet.uType of STRRET_CSTR:
s := StrRet.cStr; STRRET_OFFSET:
begin
P := @PIDL.mkid.abID[StrRet.uOffset - SizeOf(PIDL.mkid.cb)];
SetString(s, P, PIDL.mkid.cb - StrRet.uOffset);
end; STRRET_WSTR:
s := StrRet.pOleStr;
end;//case
Memol.Lines.Add(s);
end; except
on ErEOleSysError do ShowMessage('');
end;
В этом примере имя нужной папки извлекается из компонента Edit1. Получив указатель на интерфейс ishellFoider и затем интерфейс IEnumiDList, программа заполняет полученными именами файлов список Memol.Lines.
Помимо названия из большинства объектов файловой системы можно "вытащить" массу полезной информации. Чаще всего задаются вопросом: а как извлечь значок, соответствующий данному файлу или хранящийся в нем?
Способов для достижения этой цели несколько. Самый простой — через вызов функции:
function SHGetFileInfo(pszPath: PAnsiChar; dwFileAttributes: DWORD;
var psfi: TSHFilelnfo; cbFilelnfo, uFlags: UINT): DWORD;
Параметр pszPath может быть указателем как на строку с именем файла, так и на структуру вида pidl. Функция заполняет структуру psfi (тип TSHFilelnfo) длиной cbFilelnfo байт. В зависимости от значения слова флагов (параметр uFlags) на выходе может быть разнообразная информация. В частности, если в параметре uFlags заданы значения SHGFI_SYSICONINDEX и SHGFI_ICON, то в структуру psfi будет записан номер значка для данного файла в системном списке изображений, а результатом выполнения функции будет дескриптор этого списка. Воспользоваться им можно (например, для панели инструментов) так:
procedure TForml.FormCreate(Sender: TObject);
var
Filelnfo: TSHFilelnfo;
ImageListHandle: THandle;
begin
ImageListHandle := SHGetFilelnfo('С:\',
0,
Filelnfo, SizeOf(Filelnfo) ,
SHGFI_SYSICONINDEX or SHGFI_ICON);
SendMessage(ToolBarl.Handle, TB_SETIMAGELIST, 0, ImageListHandle);
end;
Точно так же можно извлечь значок, соответствующий конкретному файлу. В составе Shell есть другие функции, созданные для извлечения значков:
Эта функция извлекает значок из файла IpszExeFileName (это должен быть файл типа EXE, DLL или ICO) и возвращает его дескриптор. Если значок не найден, возвращаемое значение равно 0.
Эта функция может работать с файлами разных форматов. Сначала она, как и предыдущая, ищет значок в теле файла. Если его там нет, предпринимается попытка отыскать значок в приложении, связанном с данным типом файлов. Например, из файла с расширением doc будет извлечен один из значков Microsoft Word.