Интерфейс 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.