|
||||||||||||||||||||||||||||
|
Программирование на Visual C++Выпуск №31 от 4 февраля 2001 г. Всем привет! В Красноярске опять стоят жуткие холода, что надо сказать, уже до смерти надоело. Но точно также, как жизнь в городе – которая продолжается не смотря на температуру за бортом, продолжается и публикация различных интересных материалов в рассылке. На этот раз я хочу представить вашему вниманию статью про работу с оболочкой Windows, которую автор мне любезно разрешил опубликовать. СТАТЬЯПространство имен оболочки Windows
В операционных системах компании Microsoft с 1995 года используется новая оболочка, построенная на основе компонентной объектной модели. Одним из нововведений оболочки операционной системы стало понятие пространства имён оболочки. Пространство имён оболочки являет собой иерархически упорядоченный мир объектов, известных операционной системе, с их свойствами и предоставляемыми действиями. Оно во многом сходно со структурой файловой системы, но включает в себя не только файлы и каталоги. Такие понятия файловой системы, как имя файла и путь, заменены более универсальными. Основное пространство имён начинается с корневого объекта "Рабочий стол", и его легко исследовать, запустив приложение "Проводник". Параллельно основному пространству имён могут сосуществовать множество дополнительных пространств имён, о которых подробнее будет рассказано позднее. Основные понятияПространство имён (Shell namespace) является древовидной структурой, состоящей из COM-объектов. Объекты, владеющие дочерними объектами, именуются папками (Shell folder), причём среди таковых могут оказаться и другие папки (Subfolders). Объекты, не владеющие дочерними объектами, именуются файловыми объектами (file objects), причём файловым объектом может представлять собой не только файл файловой системы, но и принтер, компонент "Панели Управления" или объект другого типа. Каждый объект имеет идентификатор элемента (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-указатель. Все глобальные методы (утилиты) оболочки, принимающие в качестве одного из параметров PIDL-указатель, ожидают его в абсолютном формате. В то же время все методы интерфейса IShellFolder, принимающие в качестве одного из параметров pidl-указатель, ожидают его в относительном формате (если только в описании метода не указано иначе). Ниже представлена функция, позволяющая получить указатель на следующий элемент в списке идентификаторов. В случае неудачи возвращается пустой указатель. #include <shlobj.h> LPITEMIDLIST GetNextItemID(const LPITEMIDLIST pidl) { size_t cb = pidl->mkid.cb; if (cb == 0) { return NULL; } pidl = (LPITEMIDLIST)(((LPBYTE)pidl) + cb); if (pidl->mkid.cb == 0) { return NULL; } return pidl; } За размещение списков идентификаторов отвечает распределитель памяти оболочки (Shell's allocator), предоставляющий интерфейс IMalloc. Указатель на данный интерфейс распределителя памяти оболочки можно получить через метод SHGetMalloc. Таким образом, если Ваше приложение получает от оболочки PIDL-указатель, то оно становится ответственным за обязательное в дальнейшем освобождение этого списка с помощью распределителя памяти оболочки. Ниже представлен пример копирования списка идентификаторов: #include <shlobj.h> size_t GetItemIDListSize(const LPITEMIDLIST pidl) { size_t size = 0; LPBYTE p = LPBYTE(pidl); while (p != NULL) { if (static_cast(p + size)->mkid.cb == 0) { size += sizeof(USHORT); // size of terminator; break; } size += static_cast(p + size)->mkid.cb; } return size; } LPITEMIDLIST CopyItemIDList(const LPITEMIDLIST pidl) { LPMALLOC pMalloc; LPITEMIDLIST pidlResult; if (pidl == NULL) { return NULL; } if (!SUCCEEDED(SHGetMalloc(&pMalloc)) { return NULL; } size_t size = GetItemIDListSize(pidl); pidlResult = pMalloc->Alloc(size); if (pidlResult!= NULL) { CopyMemory(pidlResult, pidl, size); } pMalloc->Release(); return pidlResult; } Для увеличения эффективности работы Ваших приложений рекомендуется брать ссылку на распределитель памяти оболочки при запуске приложения, и освобождать эту ссылку при выходе из приложения. Интерфейс IShellFolder предоставляет метод CompareIDs для определения расположения двух идентификаторов относительно друг друга (выше, ниже или равны) в данной папке. При этом параметр lParam определяет критерий упорядочивания, но заранее определённым для всех объектов-папок является только сортировка по имени (значение 0). Если вызов этого метода завершён успешно, то поле CODE возвращаемого значения содержит ноль при равенстве объектов, отрицательно, если первое меньше второго, и положительно в обратном случае. hr = ppsf->CompareIDs(0, pidlA, pidlB); if (SUCCEEDED(hr)) { iComparisonResult = short(HRESULT_CODE(hr)) }Местонахождение объектов-папок Некоторые папки имеют особое значение для оболочки. Для нахождения этих специальных папок, а также для того, чтобы пользователь мог сам искать необходимые ему папки, оболочка предоставляет специализированный набор функций:
При отсутствии нужной папки может, по требованию приложения, её создавать. Навигация по пространству имёнКаждый объект-папка прдоставляет Вам возможность перебора всех объектов, которыми данный объект владеет. Для этого Вам предоставляется метод EnumObjects интерфейса IShellFolder, который возвращает интерфейс-итератор IEnumIDList. При этом Вы можете ограничить список (включать папки, не папки, скрытые и системные объекты). Описание методов интерфейса IEnumIDList:
Таким образом Вы сможете получить набор указателей на списки идентификаторов, причём эти списки будут относительными по отношению к папке-владельцу. Чтобы получить интерфейс IShellFolder для любого из этих объектов, Вам потребуется осуществить привязку, вызвав метод BindToObject интерфейса IShellFolder папки-владельца. Чтобы узнать атрибуты данного объекта или нескольких объектов, необходимо вызвать метод GetAttributesOf интерфейса IShellFolder папки-владельца. При этом перед вызовом этого метода необходимо установить те атрибуты, значения которых Вы бы хотели выяснить. Если запрошены атрибуты нескольких элементов, то метод вернёт только те значения атрибутов, которые совпадают у всех переданных элементов. В частности, Вы сможете взять интерфейс IShellFolder только от тех объектов, которые имеют атрибут SFGAO_FOLDER. Вы можете обновить информацию об элементах, входящих в папку, использовав флаг SFGAO_VALIDATE. Дополнительные возможностиПрежде всего, Ваше приложение всегда можете получить строку с именем объекта, представленном в удобном для Вас формате. Для этого интерфейс IShellFolder предоставляет метод GetDisplayNameOf. Вы можете указать один из следующих требующихся форматов:
Имя элемента, полученное с установленным флагом SHGDN_FORPARSING, имеет особое значение. Вы можете использовать такое имя как командную строку для запуска приложения. Говоря точнее – такое имя эквивалентно понятию пути файловой системы. Интерфейс IShellFolder предоставляет метод SetNameOf, позволяющий изменить экранное имя файлового объекта или вложенной папки. Изменяя экранное имя элемента, Вы изменяете его идентификатор, поэтому функция возвращает PIDL-указатель на новый идентификатор. Изменение экранного имени файлового объекта приводит к его фактическому переименованию в файловой системе. Интерфейс IShellFolder также предоставляет метод ParseDisplayName, который позволяет узнать идентификатор элемента по его имени. Этому методу необходимо передавать имя, сгенерированное методом GetDisplayNameOf с установленным флагом SHGDN_FORPARSING. С помощью глобального метода SHGetPathFromIDList по списку идентификаторов, определяющих объект относительно корня пространства имён, можно определить путь к объекту файловой системы. С помощью глобального метода SHAddToRecentDocs Ваше приложение может добавить документ к списку последних, с которыми работал пользователь, или очистить этот список. С помощью глобального метода SHEmptyRecycleBin, появившегося в версии 4.71 оболочки windows, Ваше приложение может очистить корзину (recycle bin). Удаление файла в корзину (то есть – с возможностью дальнейшего восстановления) производится глобальным методом SHFileOperation, подробное описание которого выходит за рамки этого обзора. Вы также можете узнать количество объектов, расположенных в корзине, и их суммарный размер, с помощью метода SHQueryRecycleBin. ПримечанияMicrosoft Visual C++ поставляется с файлами заголовков <comip.h> и <comdef.h> поддержки COM, в которых определены шаблон класса _com_ptr_t, инкапсулирующий функциональность ссылки на com-объект, и самые распространённые специализации этого шаблона (в том числе для большинства стандартных интерфейсов пространства имён оболочки). При их использовании освобождение ссылок автоматизируется. Copyright 1999 by Akzhan Abdulin. При публикации просьба указывать источник и авторство. Комментарии, исправления, замечания и пожелания приветствуются по адресу: akzhan@beep.ru. ВОПРОС-ОТВЕТ
В ПОИСКАХ ИСТИНЫ
Это все на сегодня. Пока! (Алекс Jenter jenter@mail.ru) (Красноярск, 2001.) |
|
||||||||||||||||||||||||||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||||||||||||||||||||||||||
|