В операционных системах компании Microsoft с 1995 года используется новая оболочка, построенная на основе компонентной объектной модели. Одним из нововведений оболочки операционной системы стало понятие пространства имен оболочки, представляющее иерархически упорядоченный мир объектов, известных операционной системе, с их свойствами и предоставляемыми действиями.
Основное пространство имен начинается с корневого объекта Рабочий стол, и его легко исследовать, запустив приложение Проводник. Параллельно основному пространству имен может сосуществовать множество дополнительных пространств имен, о которых подробнее будет рассказано позднее.
Пространство имен (Shell namespace) является древовидной структурой, состоящей из объектов COM. Объекты, владеющие дочерними объектами, именуются папками (Shell folder), причем среди таких могут оказаться и другие папки (Subfolders). Каждый объект имеет идентификатор элемента (Item identifier), определяющий его расположение в папке. Таким образом, чтобы указать на некий объект в данной папке, нам потребуется лишь передать его идентификатор. Если же мы хотим указать на некий объект в известном пространстве имен, тогда нам придется указать идентификаторы всех папок, начиная с корня, и до самого объекта включительно. В качестве примера приведем аналогию из файловой системы.
C:\Мои документы\Доклад о возможных способах реализации интерфейса к корпоративной БД.doc уникально представит файл относительно файловой системы известного (моего домашнего) компьютера.
То, что в файловой системе именуется путем к
файлу, в пространстве имен именуется списком
идентификаторов (Identifier List).
Объекты-папки знают о тех объектах, которыми они
владеют, и о тех операциях, которые с ними
возможны. Папки предоставляют нам механизм для
перечисления всех объектов, которыми данный
объект-папка владеет интерфейс IShellFolder.
Получение от объекта указателя на данный
интерфейс называется привязкой (Binding).
Большая часть объектов основного пространства
имен оболочки являются объектами,
представляющими часть файловой системы. Те же
объекты, что не представлены в файловой системе,
называются виртуальными. Такие виртуальные
папки, как папки рабочего стола (Desktop), Мой
Компьютер (My Computer) и Сетевое окружение (Network
Neighborhood), позволяют реализовать унифицированное
пространство имен. Каталоги файловой системы,
используемые оболочкой в особых целях,
называются специальными. Одной из таких папок,
например, является папка Программы (Programs).
Местонахождение специальных папок файловой
системы указывается в подразделе ветви
HKEY_CURRENT_USER/Software /Microsoft/Windows
/CurrentVersion /Explorer /Shell Folders/.
Идентификатор элемента является двоичной структурой переменного размера, чей формат определяется тем программным обеспечением, которое поддерживает существование папки, владеющей определяемым данным идентификатором объектом.
Идентификатор элемента описывается структурой
SHITEMID, для которой определено лишь значение
первого поля размер данной структуры.
Список идентификаторов, уникально
идентифицирующих объект в определенном
пространстве имен, определяется как набор
последовательно расположенных идентификаторов,
за которыми следует 16-битное значение 0x0000 (ITEMIDLIST).
Приложение оперирует понятием указателя на
список идентификаторов (Pointer to an Identifier List),
который кратко именуют как PIDL. Вы можете
посмотреть функцию, позволяющую получить
указатель на следующий элемент в списке
идентификаторов. В случае неудачи возвращается
пустой указатель на языке C++ или Delphi. За
размещение списков идентификаторов обычно
отвечает распределитель памяти оболочки Shells
allocator), предоставляющий интерфейс IMalloc. Указатель
на данный интерфейс распределителя памяти
оболочки можно получить через метод SHGetMalloc.
Таким образом, если ваше приложение получает от
оболочки PIDL, то оно становится ответственным за
освобождение этого списка с помощью
распределителя памяти оболочки.
Ниже представлен пример копирования списка
идентификаторов (функции написаны не оптимально,
но это всего лишь рабочий пример): C++ Delphi
Для увеличения эффективности работы ваших
приложений рекомендуется брать ссылку на
распределитель памяти оболочки при запуске
приложения и освобождать эту ссылку при выходе
из приложения.
Интерфейс IShellFolder предоставляет метод CompareIDs для
определения расположения двух идентификаторов
относительно друг друга (выше, ниже или равны) в
данной папке. При этом могут использоваться
различные критерии упорядочивания, но заранее
определенным для всех объектов-папок является
только сортировка по имени (значение 0).
Некоторые папки имеют особое значение для оболочки. Чтобы найти эти специальные папки, а также, чтобы пользователь мог сам искать необходимые ему папки, оболочка предоставляет специализированный набор функций:
Для указания необходимой специальной папки функции SHGetSpecialFolderLocation и SHGetSpecialFolderPath принимают в качестве параметра одно из ниже указанных значений:
Функция SHBrowseForFolder позволяет ограничить видимое пространство имен, задав в поле pidlRoot корневую папку, с которой будет идти просмотр (по умолчанию Рабочий стол), и указав типы объектов, которые приемлемы в качестве выбранных, в поле ulFlags.
Описание флагов, которые допустимы в поле ulFlags:
Пример работы с функцией SHBrowseForFolder на Delphi можно посмотреть тут. Очень часто приложение использует функцию обратного вызова, чтобы указать исходную папку для просмотра. В примере использовался механизм обратного вызова именно для этой цели, а также для того, чтобы установить некий текст при инициализации диалога.
Каждый объект-папка предоставляет вам возможность перебора всех объектов, которыми данный объект владеет. Для этого вам предоставляется метод EnumObjects интерфейса IShellFolder, который возвращает интерфейс-итератор IEnumIDList. При этом вы можете ограничить список (включать папки, не папки, скрытые и системные объекты).
Описание методов интерфейса IEnumIDList:
Таким образом, вы сможете получить набор указателей на списки идентификаторов, причем эти списки будут относительными по отношению к папке-владельцу. Чтобы получить интерфейс IShellFolder для любого из этих объектов, вам потребуется осуществить привязку, вызвав метод BindToObject интерфейса IShellFolder папки-владельца. Чтобы узнать атрибуты данного объекта или нескольких объектов, необходимо вызвать метод GetAttributesOf интерфейса IShellFolder папки-владельца. При этом перед вызовом этого метода необходимо установить те атрибуты, значения которых вы бы хотели выяснить. Если запрошены атрибуты нескольких элементов, то метод вернет только те значения атрибутов, которые совпадают у всех переданных элементов. В частности, вы сможете взять интерфейс IShellFolder только от тех объектов, которые имеют атрибут SFGAO_FOLDER. Вы можете обновить информацию об элементах, входящих в папку, использовав флаг SFGAO_VALIDATE. Вам представлен пример навигации по основному пространству имен на с++ и Delphi.
Прежде всего, ваше приложение всегда может получить строку с именем объекта, представленном в удобном для вас формате. Для этого интерфейс IShellFolder предоставляет метод GetDisplayNameOf. Вы можете указать один из следующих требующихся форматов:
Имя элемента, полученное с установленным флагом SHGDN_FORPARSING, имеет особое значение. Вы можете использовать такое имя как командную строку для запуска приложения. Говоря точнее такое имя эквивалентно понятию пути файловой системы. Интерфейс IShellFolder предоставляет метод SetNameOf, позволяющий изменить экранное имя файлового объекта или вложенной папки. Изменяя экранное имя элемента, вы изменяете его идентификатор, поэтому функция возвращает PIDL-указатель на новый идентификатор. Изменение экранного имени файлового объекта приводит к его фактическому переименованию в файловой системе.
Интерфейс IShellFolder также предоставляет метод ParseDisplayName, который позволяет узнать идентификатор элемента по его имени. Этому методу необходимо передавать имя, сгенерированное методом GetDisplayNameOf с установленным флагом SHGDN_FORPARSING.
С помощью глобального метода SHGetPathFromIDList по
списку идентификаторов, определяющих объект
относительно корня пространства имен, можно
определить путь к объекту файловой системы. С
помощью глобального метода SHAddToRecentDocs ваше
приложение может добавить документ к списку
последних, с которыми работал пользователь, или
очистить этот список.
С помощью глобального метода SHEmptyRecycleBin,
появившегося в версии 4.71 оболочки Windows, ваше
приложение может очистить корзину (Recycle Bin).
Удаление файла в корзину (то есть с
возможностью дальнейшего восстановления)
производится глобальным методом SHFileOperation,
подробное описание которого выходит за рамки
этого обзора.
Поиск интерфейсов, позволяющих оперировать с данным объектом, можно осуществить через метод GetUIObjectOf интерфейса IShellFolder (обычно запрашиваются интерфейсы контекстных меню и операций drag-and-drop).
Частой задачей является выполнение некоторых
команд контекстного меню для данного элемента.
Например, для открытия окна свойств объекта
нужно запросить интерфейс IContextMenu этого объекта и
активизировать команду Properties. Можете
посмотреть пример реализации такой задачи на с++
и Delphi.
Все примеры в данном обзоре являются реально
работающими (предварительно скомпилированными).
Для компиляции использовались Microsoft Visual C++ 6.0 SP3 и
Borland Delphi 4.0 UP3.