Пространство имён оболочки Windows

Пространство имён оболочки Windows Автор: Акжан Абдулин Введение В операционных системах компании Microsoft с 1995 года используется новая оболочка, построенная на основе компонентной объектной модели. Одним из нововведений оболочки операционной системы стало понятие пространства имён оболочки. Пространство имён оболоч ки являет собой иерархически упорядоченный мир объектов, известных операционной системе, с их свойствами и предоставляемыми действиями. Оно во многом сходно со структурой файловой системы, но включает в себя не только файлы и каталоги. Такие понятия файло вой системы, как имя файла и путь, заменены более универсальными. Основное пространство имён начинается с корневого объекта "Рабочий стол", и его легко исследовать, запустив приложение "Проводник". Параллельно основному пространству имён могут сосуществовать множество дополнительных пространств имён, о которых подробнее будет рассказано позднее. Основные понятия Пространство имён (Shell namespace) является древовидной структурой, состоящей из COM-объектов. Объекты, владеющие дочерними объектами, именуются папками (Shell folder), причём среди таковых могут оказаться и другие папки (Subfolders). Объекты, не владеющ ие дочерними объектами, именуются файловыми объектами (file objects), причём файловым объектом может представлять собой не только файл файловой системы, но и принтер, компонент "Панели Управления" или объект другого типа. Каждый объект имеет идентификатор элемента (Item identifier), однозначно определяющий его расположение в папке. Таким образом, чтобы указать на некий объект в данной папке, нам потребуется лишь передать его идентификатор. Если же мы хотим указать на некий объект в известном пространстве имён, тогда нам придётся указать идентификаторы всех папок, начиная с корня, и до самого объекта включительно. В качестве примера приведём аналогию из файловой системы: "C:\Мои документы\Доклад о возможных способах реализации интерфейса к корпоративной БД.doc" уникально представит файл относительно файловой системы известного (моего домашнего) компьютера. То, что в файловой системе именуется путём к файлу, в пространстве имён именуется списком идентификаторов (Identifier List). Объекты-папки знают о тех обьектах, которыми они владеют, и о тех операциях, которые с ними возможны. Папки предоставляют нам механизм для перечисления всех объектов, которыми данный объект-папка владеет - интерфейс IShellFolder. Получение от объекта указ ателя на данный интерфейс называется привязкой (Binding). Большая часть объектов основного пространства имён оболочки являются объектами, представляющими часть файловой системы. Те же объекты, что не представлены в файловой системе, называются виртуальными. Такие виртуальные папки, как папки рабочего стола (Desk top), "Мой Компьютер" (My Computer) и "Сетевое окружение" (Network Neighborhood), позволяют реализовать унифицированное пространство имён. Каталоги файловой системы, используемые оболочкой в особых целях, называются специальными. Одной из таких папок, например, является папка "Программы" (Programs). Местонахождение специальных папок файловой системы указывается в подразделе ветви HKEY_CURREN T_USER/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/. Идентификаторы элементов Идентификатор элемента является уникальным для той папки, в которой данный элемент (объект пространства имён оболочки) находится, и является двоичной структурой переменного размера, чей формат определяется тем программным обеспечением, которое поддерживае т существование папки, владеющей определяемым данным идентификатором объектом. Идентификатор элемента имеет смысл только в контексте той папки, которая его сконструировала. Идентификатор элемента описывается структурой SHITEMID, для которой определено лишь значение первого поля - размер данной структуры. Список идентификаторов, уникально идентифицирующих объект в определённом пространстве имён, эквивалентен понятию пути для файловой системы, и определяется как список из последовательно расположенных идентификаторов, за которыми следует завершающее список 16-битное значение 0x0000 (ITEMIDLIST). Список идентификаторов может быть как абсолютным, то есть определяющим положение объекта относительно корневой папки, так и относительным, то есть определяющим положение элемента относительно какой-либо конкретной п апки. Приложение оперирует понятием указателя на список идентификаторов (Pointer to an Identifier List), который кратко именуют как PIDL-указатель. Все глобальные методы (утилиты) оболочки, принимающие в качестве одного из параметров PIDL-указатель, ожидают его в абсолютном формате. В то же время все методы интерфейса IShellFolder, принимающие в качестве одного из параметров PIDL-указатель, ожидают его в относительном формате (если только в описании метода не указано иначе). Ниже представлена функция, позволяющая получить указатель на следующий элемент в списке идентификаторов. В случае неудачи возвращается пустой указатель.

// C/C++
#include
LPITEMIDLIST GetItemID(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;
}

// Delphi
uses ShlObj;
type
 PByte = ^Byte;
function GetItemID(pidl: PItemIDList): PItemIDList;
var
 cb: Integer;
begin
 cb := pidl^.mkid.cb;
 if cb = 0 then
 begin
  Result := nil;
  Exit;
 end;
 pidl := PItemIDList ( Integer(PByte(pidl)) + cb );
 if pidl^.mkid.cb = 0 then
 begin
  Result := nil;
  Exit;
 end;
 Result := pidl;
end;
За размещение списков идентификаторов отвечает распределитель памяти оболочки (Shell's allocator), предоставляющий интерфейс IMalloc. Указатель на данный интерфейс распределителя памяти оболочки можно получить через метод SHGetMalloc. Таким образом, если Ваше приложение получает от оболочки PIDL-указатель, то оно становится ответственным за обязательное в дальнейшем освобождение этого списка с помощью распределителя памяти оболочки. Ниже представлен пример копирования списка идентификаторов:
// C++
#include
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;
}

// Delphi
uses Windows, ActiveX, ShlObj;
type
 PByte = ^Byte;
function GetItemIDListSize(pidl: PItemIDList): Integer;
var
 p: PChar;
begin
 Result := 0;
 p := PChar(pidl);
 while p <> nil do
 begin
  if PItemIDList(p + Result)^.mkid.cb = 0 then
  begin
  Inc( Result, sizeof(Word) ); // size of terminator;
  break;
  end;
  Inc( Result, PItemIDList(p + Result)^.mkid.cb );
 end;
end;
function CopyItemIDList(pidl: PItemIDList): PItemIDList;
var
 pMalloc: IMalloc;
 size: Integer;
begin
 Result := nil;
 if not Assigned( pidl ) then
 begin
  Exit;
 end;
 if not SUCCEEDED(SHGetMalloc(pMalloc)) then
 begin
  Exit;
 end;
 size := GetItemIDListSize(pidl);
 Result := pMalloc.Alloc( size );
 if Assigned(Result) then
 begin
  CopyMemory( Result, pidl, size );
 end;
 pMalloc := nil; // Interface reference releasing
  // will be proceed automatically
  // by assigning a nil value
end;
Для увеличения эффективности работы Ваших приложений рекомендуется брать ссылку на распределитель памяти оболочки при запуске приложения, и освобождать эту ссылку при выходе из приложения. Интерфейс IShellFolder предоставляет метод CompareIDs для определения расположения двух идентификаторов относительно друг друга (выше, ниже или равны) в данной папке. При этом параметр lParam определяет критерий упорядочивания, но заранее определённым для всех объектов-папок является только сортировка по имени (значение 0). Если вызов этого метода завершён успешно, то поле CODE возвращаемого значения содержит ноль при равенстве объектов, отрицательно, если первое меньше второго, и положительно в обратном случае.
// C/C++
 hr = ppsf->CompareIDs(0, pidlA, pidlB);
 if( SUCCEEDED(hr) )
 {
  iComparisonResult = short(HRESULT_CODE(hr))
 }



// Delphi
function HRESULT_CODE(hr: HRESULT): Word;
begin
 Result := Word(LongWord(hr) and $FFFF)
end;
 hr := ppsf.CompareIDs(0, pidlA, pidlB);
 if Succeeded(hr) then
 begin
  iComparisonResult := Shortint(HRESULT_CODE(hr));
 end;

Местонахождение объектов-папок
Некоторые папки имеют особое значение для оболочки. Для нахождения этих специальных папок, а также для того, чтобы пользователь мог сам искать необходимые ему папки, оболочка предоставляет специализированный набор функций: SHGetDesktopFolder Возвращает ин терфейс IShellFolder объекта-папки "Рабочий стол" (Desktop);

SHGetSpecialFolderLocation Возвращает указатель на список идентификаторов специального объекта-папки.
SHBrowseForFolder Проводит диалог с пользователем и возвращает указатель на список идентификаторов выбранного пользователем объекта-папки;
SHGetSpecialFolderPath Версия 4.71. Возвращает путь файловой системы для специального объекта-папки. Функция предназначена для работы со специальными папками, а не для работы с виртуальными.
При отсутствии нужной папки может, по требованию приложения, её создавать.
Для указания необходимой специальной папки функции SHGetSpecialFolderLocation и SHGetSpecialFolderPath принимают в качестве параметра одно из ниже указанных значений:
CSIDL_DESKTOP Рабочий стол (Desktop) для данного пользователя; Виртуальная папка, являющаяся корнем основного пространства имён оболочки
CSIDL_INTERNET Интернет (Internet); Виртуальная папка, представляющая пространство Internet
CSIDL_PROGRAMS Программы (Programs) для данного пользователя; Каталог файловой системы, содержащий в себе группы программ пользователя, также являющиеся каталогами файловой системы
CSIDL_CONTROLS Панель управления (Control Panel); Виртуальная папка, содержащая в себе набор иконок панели управления
CSIDL_PRINTERS Принтеры (Printers); Виртуальная папка, содержащая в себе инсталлированные принтеры
CSIDL_PERSONAL Мои документы (My Documents); Каталог файловой системы, служащий общим репозиторием для документов
CSIDL_FAVORITES Избранное (Favorites) для данного пользователя; Каталог файловой системы, служащий общим репозиторием избранных пользователем элементов
CSIDL_STARTUP Автозагрузка (Startup) для данного пользователя; Каталог файловой системы, который является пользовательской папкой программ "Автозагрузка". Система запускает эти программы каждый раз, когда данный пользователь входит в Windows NT, или когда стартует Windows 95/98
CSIDL_RECENT Документы (Documents); Каталог файловой системы, содержащий в себе ссылки на самые последние документы, с которыми недавно работал пользователь
CSIDL_SENDTO Отправить (Send To); Каталог файловой системы, содержащий в себе пункты меню Send To
CSIDL_BITBUCKET Корзина (Recycle Bin);
CSIDL_STARTMENU Главное меню (Start menu) для данного пользователя; Каталог файловой системы, содержащий в себе пункты меню Start
CSIDL_DESKTOPDIRECTORY Каталог файловой системы, хранящий файловые объекты Рабочего стола (Desktop directory) для данного пользователя;
CSIDL_DRIVES Мой компьютер (My computer); Виртуальная папка, содержащая в себе всё, что находится на локальном компьютере: устройства хранения, принтеры и панель управления. Эта папка может также содержать в себе спроецированные сетевые диски
CSIDL_NETWORK Сетевое окружение (Network Neighborhood); Виртуальная папка, представляющая верхний уровень иерархии сети
CSIDL_NETHOOD Каталог файловой системы, хранящий файловые объекты Сетевого окружения (Network Neighborhood);
CSIDL_FONTS Шрифты (Fonts); Виртуальная папка, содержащая шрифты
CSIDL_TEMPLATES Шаблоны (Templates); Каталог файловой системы, служащий общим репозиторием шаблонов документов (пункт контекстного меню оболочки "Создать")
CSIDL_COMMON_STARTMENU Каталог файловой системы, содержащий в себе общие пункты меню Start, которые появляются у всех пользователей;
CSIDL_COMMON_PROGRAMS Каталог файловой системы, содержащий в себе общие группы программ пользователя, которые появляются у всех пользователей;
CSIDL_COMMON_STARTUP Каталог файловой системы, содержащий в себе общие программы, которые появляются в папке Startup для всех пользователей;
CSIDL_COMMON_DESKTOPDIRECTORY Каталог файловой системы, хранящий общие файловые объекты Рабочего стола (Desktop directory), которые появляются на рабочих столах всех пользователей;
CSIDL_APPDATA Каталог файловой системы, служащий общим репозиторием данных, специфичных для приложения;
CSIDL_PRINTHOOD Каталог файловой системы, служащий общим репозиторием ссылок на принтеры;
CSIDL_ALTSTARTUP Каталог файловой системы, который является нелокализованной пользовательской папкой программ "Автозагрузка".
CSIDL_COMMON_ALTSTARTUP Каталог файловой системы, содержащий в себе общие программы, которые появляются в нелокализованной папке Startup для всех пользователей;
CSIDL_COMMON_FAVORITES Каталог файловой системы, содержащий в себе общие избранные элементы, которые появляются в папке "Избранное" у всех пользователей;
CSIDL_INTERNET_CACHE Каталог файловой системы, служащий общим репозиторием для временного хранения файлов, кэшируемых при работе с Internet;
CSIDL_COOKIES Каталог файловой системы, служащий общим репозиторием для Internet Cookies;
CSIDL_HISTORY Каталог файловой системы, служащий общим репозиторием для хранения истории работы с Internet;
CSIDL_PROGRAM_FILES Версия 5.00. Каталог файловой системы, в котором должны располагаться программные продукты;
CSIDL_PROGRAM_FILES_COMMON Версия 5.00. Каталог файловой системы, в котором должны располагаться компоненты, общие для группы продуктов.
Необходимо отметить, что эти функции недавно были выделены в отдельную библиотеку - shfolder.dll.
Функция SHBrowseForFolder позволяет ограничить видимое пространство имён, задав в поле pidlRoot корневую папку, с которой будет идти просмотр (по умолчанию - "Рабочий стол"), и указав типы объектов, которые приемлемы в качестве выбранных, в поле ulFlags.
Описание флагов, которые допустимы в поле ulFlags: BIF_BROWSEFORCOMPUTER В качестве выбора допустимы только компьютеры.
BIF_BROWSEFORPRINTER В качестве выбора допустимы только принтеры.
BIF_BROWSEINCLUDEFILES В диалоговом окне, помимо папок, будут также представлены и файлы.
BIF_DONTGOBELOWDOMAIN Не показывать сетевые папки, расположенные ниже уровня домена.
BIF_EDITBOX Версия 4.71. В диалоговом окне будет присутствовать строка редактирования, таким образом пользователь может набрать имя элемента.
BIF_RETURNFSANCESTORS В качестве выбора допустимы только объекты, представленные в файловой системе.
BIF_RETURNONLYFSDIRS В качестве выбора допустимы только каталоги файловой системы.
BIF_STATUSTEXT В диалоговом окне появится строка статуса, функция обратного вызова сожет устанавливать текст в этой строке с помощью посылки сообщений диалоговому окну.
BIF_VALIDATE Версия 4.71. Если пользователь введёт неверное имя в строке редактирования, то диалоговое окно вызовет функцию обратного вызова приложения по сообщению BFFM_VALIDATEFAILED.
Ниже представлен пример работы с функцией SHBrowseForFolder на Delphi. Очень часто приложение использует функцию обратного вызова, чтобы указать исходную папку для просмотра. Здесь мы будем использовать механизм обратного вызова именно для этой цели, а та кже для того, чтобы установить некий текст при инициализации диалога:

// Delphi

(*

 * Эта программа показывает на экране диалог выбора папки,

 * где корнем дерева им?н служит папка "Мой компьютер", а текущим

 * каталогом - папка "Файлы программ".

 *)


program ShellD;

uses

 SysUtils, Windows, ActiveX, ShlObj;

var

 Malloc: IMalloc;

 Desktop: IShellFolder;

 pidlMyComputer: PItemIDList;

 pidlResult: PItemIDList;

 pidlInitialFolder: PItemIDList;

function BrowseCallbackProc( hWnd: HWND; uMsg: UINT; lParam: LPARAM;

 lpData: LPARAM ): Integer; stdcall; // обратите внимание на соглашение о вызовах -

  // stdcall

begin

 Result := 0;

 case uMsg of

  BFFM_INITIALIZED:

  begin

  PostMessage( hWnd, BFFM_SETSELECTION, 0, Integer(pidlInitialFolder) );

  PostMessage( hWnd, BFFM_SETSTATUSTEXT, 0,

  Integer(PChar('Функция обратного вызова установлена.')) );

  end;

 end;

end;

function GetProgramFilesDirByKeyStr(KeyStr: string): string;

var

 dwKeySize: DWORD;

 Key: HKEY;

 dwType: DWORD;

begin

 if

  RegOpenKeyEx( HKEY_LOCAL_MACHINE, PChar(KeyStr), 0, KEY_READ, Key ) = ERROR_SUCCESS

 then

 try

  RegQueryValueEx( Key, 'ProgramFilesDir', nil, @dwType, nil, @dwKeySize );

  if (dwType in [REG_SZ, REG_EXPAND_SZ]) and (dwKeySize > 0) then

  begin

  SetLength( Result, dwKeySize );

  RegQueryValueEx( Key, 'ProgramFilesDir', nil, @dwType, PByte(PChar(Result)),

  @dwKeySize );

  end

  else

  begin

  RegQueryValueEx( Key, 'ProgramFilesPath', nil, @dwType, nil, @dwKeySize );

  if (dwType in [REG_SZ, REG_EXPAND_SZ]) and (dwKeySize > 0) then

  begin

  SetLength( Result, dwKeySize );

  RegQueryValueEx( Key, 'ProgramFilesPath', nil, @dwType, PByte(PChar(Result)),

  @dwKeySize );

  end;

  end;

 finally

  RegCloseKey( Key );

 end;

end;

// Here is old way to retrieve Program Files folder location,

// Modern way is using of SHGetSpecialFolder (shfolder.dll) with

// CSIDL_PROGRAM_FILES constant.

function GetProgramFilesDir: string;

const

 DefaultProgramFilesDir = '%SystemDrive%\Program Files';

var

 FolderName: string;

 dwStrSize: DWORD;

begin

 if Win32Platform = VER_PLATFORM_WIN32_NT then

 begin

  FolderName :=

  GetProgramFilesDirByKeyStr('Software\Microsoft\Windows NT\CurrentVersion');

 end;

 if Length(FolderName) = 0 then

 begin

  FolderName :=

  GetProgramFilesDirByKeyStr('Software\Microsoft\Windows\CurrentVersion');

 end;

 if Length(FolderName) = 0 then FolderName := DefaultProgramFilesDir;

 dwStrSize := ExpandEnvironmentStrings( PChar(FolderName), nil, 0 );

 SetLength( Result, dwStrSize );

 ExpandEnvironmentStrings( PChar(FolderName), PChar(Result), dwStrSize );

end;

var

 bi: TBrowseInfo;

 DisplayName: string;

 ProgramFilesDir: WideString;

 CharsDone: ULONG;

 dwAttributes: DWORD;

 Temp: string;

begin

 ProgramFilesDir := GetProgramFilesDir;

 // acquire shell's allocator

 if SUCCEEDED( SHGetMalloc( Malloc ) ) then

 try

  // acquire shell namespace root folder

  if SUCCEEDED( SHGetDesktopFolder( Desktop ) ) then

  try

  // acquire folder that will serve as root in dialog

  if SUCCEEDED( SHGetSpecialFolderLocation( 0, CSIDL_DRIVES, pidlMyComputer ) ) then

  try

  // acquire PIDL for folder that will be selected by default

  if

  SUCCEEDED(

  Desktop.ParseDisplayName( 0, nil, PWideChar(ProgramFilesDir), CharsDone,

  pidlInitialFolder, dwAttributes )

  )

  then

  try

  SetLength( DisplayName, MAX_PATH );

  FillChar( bi, sizeof(bi), 0 );

  bi.pidlRoot := pidlMyComputer; // roots from 'My Computer'

  bi.pszDisplayName := PChar( DisplayName );

  bi.lpszTitle := PChar('Выберите папку');

  bi.ulFlags := BIF_STATUSTEXT;

  bi.lpfn := BrowseCallbackProc;

  pidlResult := SHBrowseForFolder( bi );

  if Assigned(pidlResult) then

  try

  SetLength( Temp, MAX_PATH );

  if SHGetPathFromIDList( pidlResult, PChar(Temp) ) then

  begin

  DisplayName := Temp;

  end;

  DisplayName := Trim(DisplayName) + '.';

  MessageBox( 0, PChar(DisplayName), 'Вы успешно выбрали папку',

  MB_OK or MB_ICONINFORMATION );

  finally

  Malloc.Free( pidlResult ); // release returned value

  end;

  finally

  Malloc.Free( pidlInitialFolder ); // release PIDL for folder that

  // was selected by default

  end;

  finally

  Malloc.Free( pidlMyComputer ); // release folder that was served as root in dialog

  end;

  finally

  Desktop := nil; // release shell namespace root folder

  end;

 finally

  Malloc := nil; // release shell's allocator

 end;

end.

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...