div.main {margin-left: 20pt; margin-right: 20pt}Пространство имен оболочки Windows
В операционных системах компании 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/C++ #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; }
|
//
Delphi uses ShlObj;
type PByte
= ^Byte;
function GetNextItemID(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 <shlobj.h>
size_t
GetItemIDListSize(const LPITEMIDLIST
pidl) { size_t size =
0; while( pidl != NULL
) { if(
pidl->mkid.cb == 0
) { size
+= sizeof( USHORT ); // size of
terminator; break; } size
+=
pidl->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; begin Result :=
0;
while pidl <> nil
do begin if
pidl^.mkid.cb = 0
then begin Inc(
Result, sizeof(USHORT) ); // size of
terminator; break; end; Inc(
Result, pidl^.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 для
определения расположения двух идентификаторов относительно
друг друга (выше, ниже или равны) в данной папке. При этом
могут использоваться различные критерии упорядочивания, но
заранее определённым для всех объектов-папок является только
сортировка по имени (значение 0).
Местонахождение объектов-папок
Некоторые папки имеют особое значение для оболочки. Чтобы
найти эти специальные папки, а также чтобы пользователь мог
сам искать необходимые ему папки, оболочка предоставляет
специализированный набор функций:
SHGetDesktopFolder |
SHGetSpecialFolderLocation |
SHBrowseForFolder |
SHGetSpecialFolderPath |
Для указания необходимой специальной папки функция
SHGetSpecialFolderLocation принимает в качестве параметра одно
из ниже указанных значений:
CSIDL_DESKTOP |
CSIDL_INTERNET |
CSIDL_PROGRAMS |
CSIDL_CONTROLS |
CSIDL_PRINTERS |
CSIDL_PERSONAL |
CSIDL_FAVORITES |
CSIDL_STARTUP |
CSIDL_RECENT |
CSIDL_SENDTO |
CSIDL_BITBUCKET |
CSIDL_STARTMENU |
CSIDL_DESKTOPDIRECTORY |
CSIDL_DRIVES |
CSIDL_NETWORK |
CSIDL_NETHOOD |
CSIDL_FONTS |
CSIDL_TEMPLATES |
CSIDL_COMMON_STARTMENU |
CSIDL_COMMON_PROGRAMS |
CSIDL_COMMON_STARTUP |
CSIDL_COMMON_DESKTOPDIRECTORY |
CSIDL_APPDATA |
CSIDL_PRINTHOOD |
CSIDL_ALTSTARTUP |
CSIDL_COMMON_ALTSTARTUP |
CSIDL_COMMON_FAVORITES |
CSIDL_INTERNET_CACHE |
CSIDL_COOKIES |
CSIDL_HISTORY |
Функция SHBrowseForFolder позволяет ограничить видимое
пространство имён, задав в поле pidlRoot корневую папку, с
которой будет идти просмотр (по умолчанию - "Рабочий стол"), и
указав типы объектов, которые приемлемы в качестве выбранных,
в поле ulFlags.
Описание флагов, которые допустимы в поле ulFlags:
BIF_BROWSEFORCOMPUTER |
BIF_BROWSEFORPRINTER |
BIF_BROWSEINCLUDEFILES |
BIF_DONTGOBELOWDOMAIN |
BIF_EDITBOX |
BIF_RETURNFSANCESTORS |
BIF_RETURNONLYFSDIRS |
BIF_STATUSTEXT |
BIF_VALIDATE |
Ниже представлен пример работы с функцией SHBrowseForFolder
на Delphi. Очень часто приложение использует функцию обратного
вызова, чтобы указать исходную папку для просмотра. Здесь же
мы будем использовать механизм обратного вызова, чтобы
разрешать к выбору только те папки, которые не имеют атрибута
SFGAO_HASSUBFOLDER (для компиляции данного примера создайте в
среде Delphi новое приложение с одной формой, сохраните эту
форму в файле с именем MainFormUnit, бросьте на форму кнопку
(TButton), и скопируйте данный код в файл MainFormUnit.pas):
// Delphi
unit MainFormUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure BrowseCB(hwnd: HWND; uMsg: UINT; lParam: LPARAM);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
ActiveX, ShlObj;
{$R *.DFM}
function ParsePIDLForOwnerID( pidl: PItemIDList ): PItemIDList;
var
temp: PItemIDList;
begin
Result := nil;
if Assigned( pidl ) then
begin
while true do
begin
temp := PItemIDList( Integer(pidl) + pidl^.mkid.cb );
if temp^.mkid.cb = 0 then break;
Result := pidl;
pidl := temp;
end;
end;
end;
type
USHORT = Word;
PByte = ^Byte;
PUSHORT = ^USHORT;
function GetOwnerPIDL( pidl: PItemIDList ): PItemIDList;
var
Divider: PItemIDList;
pMalloc: IMalloc;
Size: Integer;
begin
Result := nil;
Divider := ParsePIDLForOwnerID( pidl );
if not Assigned(Divider) then Exit;
Size := Integer(Divider) - Integer(pidl) + Divider^.mkid.cb + sizeof(USHORT);
if not SUCCEEDED( SHGetMalloc( pMalloc ) ) then Exit;
try
Result := PItemIDList(pMalloc.Alloc(Size));
if Assigned(Result) then
begin
CopyMemory( Result, pidl, Size - sizeof(USHORT) );
PUSHORT( Integer(Result) + Size - sizeof(USHORT) )^ := 0;
end;
finally
pMalloc := nil;
end;
end;
// returns IShellFolder interface of folder that owns pidlObject
// and returns relative ID (as part of pidlObject, so You need not to free it.
function GetParentFolderAndRelativeId( pidlObject: PItemIDList; var pidlRelative: PItemIDList ): IShellFolder;
var
desktop: IShellFolder;
pMalloc: IMalloc;
pidlOwner: PItemIDList;
pidlDivider: PItemIDList;
begin
Result := nil;
pidlRelative := nil;
if not SUCCEEDED( SHGetMalloc( pMalloc ) ) then
begin
Exit;
end;
try
if not SUCCEEDED( SHGetDesktopFolder( desktop ) ) then
begin
Exit;
end;
pidlOwner := GetOwnerPIDL( pidlObject );
if not Assigned( pidlOwner ) then
begin
pidlRelative := pidlObject;
Result := desktop;
Exit;
end;
try
if not SUCCEEDED( desktop.BindToObject( pidlObject, nil, IID_IShellFolder, Pointer(Result) ) ) then
begin
Exit;
end;
pidlDivider := ParsePIDLForOwnerID( pidlObject );
if Assigned( pidlDivider ) then
begin
pidlRelative := PItemIDList( Integer(pidlDivider)+pidlDivider^.mkid.cb );
end;
finally
desktop := nil;
pMalloc.Free( pidlOwner );
end;
finally
pMalloc := nil;
end;
end;
function BrowseCallbackProc(
hwnd: HWND;
uMsg: UINT;
lParam: LPARAM;
lpData: LPARAM
): Integer; stdcall;
begin
TForm1(Pointer(lpData)).BrowseCB( hwnd, uMsg, lParam );
Result := 0;
end;
procedure TForm1.BrowseCB(hwnd: HWND; uMsg: UINT; lParam: LPARAM);
var
pMalloc: IMalloc;
pidl: PItemIDList;
pidlRel: PItemIDList;
owner: IShellFolder;
attrs: ULONG;
begin
case uMsg of
BFFM_INITIALIZED: PostMessage( hwnd, BFFM_SETSTATUSTEXT, 0, Integer(PChar('Функция обратного вызова установлена.')));
BFFM_SELCHANGED :
begin
pidl := PItemIDList( lParam );
attrs := SFGAO_CONTENTSMASK;
owner := GetParentFolderAndRelativeId( pidl, pidlRel );
try
if not SUCCEEDED( owner.GetAttributesOf( 1, pidlRel, attrs ) ) then
begin
Application.MessageBox( 'CB: Не взяли атрибуты выбранной папки', 'Ошибка', 0 );
Exit;
end;
if ( attrs and SFGAO_CONTENTSMASK ) = SFGAO_HASSUBFOLDER then
begin
PostMessage( hwnd, BFFM_ENABLEOK, 0, 0 );
end
else
begin
PostMessage( hwnd, BFFM_ENABLEOK, 0, 1 );
end;
finally
owner := nil;
end;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
bi: TBrowseInfo;
pMalloc: IMalloc;
pidl: PItemIDList;
pidlResult: PItemIDList;
DisplayName: string;
begin
if not SUCCEEDED( SHGetMalloc( pMalloc ) ) then
begin
Application.MessageBox( 'Не взяли аллокатор памяти оболочки', 'Ошибка', 0 );
Exit;
end;
try
if not SUCCEEDED( SHGetSpecialFolderLocation( Handle, CSIDL_DRIVES, pidl ) ) then
begin
Application.MessageBox( 'Не нашли папку "Мой компьютер"', 'Ошибка', 0 );
Exit;
end;
try
FillMemory( @ bi, sizeof( bi ), 0 );
SetLength( DisplayName, MAX_PATH );
with bi do
begin
hwndOwner := Handle;
pidlRoot := pidl;
pszDisplayName := PChar( DisplayName );
lpszTitle := 'Выберите папку';
ulFlags := BIF_STATUSTEXT;
lpfn := BrowseCallbackProc;
lParam := Integer( Self );
end;
pidlResult := SHBrowseForFolder( bi );
if Assigned(pidlResult) then pMalloc.Free( pidlResult );
finally
pMalloc.Free( pidl );
end;
finally
pMalloc := nil;
end;
end;
end.
В вышеприведённом коде есть некоторые моменты, которые
будут разьяснены ниже.
Навигация по пространству имён
Каждый объект-папка прдоставляет Вам возможность перебора
всех объектов, которыми данный объект владеет. Для этого Вам
предоставляется метод EnumObjects интерфейса IShellFolder,
который возвращает интерфейс-итератор IEnumIDList. При этом Вы
можете ограничить список (включать папки, не папки, скрытые и
системные объекты).
Описание методов интерфейса IEnumIDList:
Таким образом Вы сможете получить набор указателей на
списки идентификаторов, причём эти списки будут относительными
по отношению к папке-владельцу.
Чтобы получить интерфейс IShellFolder для любого из этих
объектов, Вам потребуется осуществить привязку, вызвав метод
BindToObject интерфейса IShellFolder папки-владельца.
Чтобы узнать атрибуты данного объекта или нескольких
объектов, необходимо вызвать метод GetAttributesOf интерфейса
IShellFolder папки-владельца. При этом перед вызовом этого
метода необходимо установить те атрибуты, значения которых Вы
бы хотели выяснить. Если запрошены атрибуты нескольких
элементов, то метод вернёт только те значения атрибутов,
которые совпадают у всех переданных элементов. В частности, Вы
сможете взять интерфейс IShellFolder только от тех объектов,
которые имеют атрибут SFGAO_FOLDER. Вы можете обновить
информацию об элементах, входящих в папку, использовав флаг
SFGAO_VALIDATE. Ниже представлен пример навигации по основному
пространству имён:
// C++
// Это консольное приложение печатает на стандартный вывод дерево папок
// основного пространства имён оболочки Windows.
#define _WIN32_DCOM
#include <objbase.h>
#include <shlobj.h>
#include <string>
#include <iostream>
using namespace std;
// Эта процедура производит вывод строки, попутно конвертируя её из кодировки ANSI в кодировку OEM.
void WriteStr(const char * s)
{
char * s1 = strdup( s );
CharToOemBuff( s1, s1, strlen(s1) );
cout << s1;
free( reinterpret_cast<void *>( s1 ) );
}
// Эта процедура производит вывод указанного количества символов, попутно конвертируя их из кодировки ANSI в кодировку OEM.
void WriteChar(char ch, size_t count)
{
CharToOemBuff( &ch, &ch, 1 );
for(int i=0; i<count; i++) cout << ch;
}
// Эта процедура производит вывод строки, попутно конвертируя её из кодировки ANSI в кодировку OEM.
// Эта процедура также добавляет перевод каретки по окончании вывода.
void WritelnStr(const char * s)
{
char chrs[2] = { ' ', ' ' };
WriteStr( s );
chrs[0] = 'n';
WriteStr( chrs );
chrs[0] = 'r';
WriteStr( chrs );
}
LPMALLOC pMalloc = NULL;
HRESULT hr;
// Эта процедура интерпретирует содержание структуры STRRET.
// Также она освобождает временные буфера при необходимости, так что
// для её корректной работы требуется наличие объекта pMalloc.
// После завершения работы с возвращаемым именем необходимо освободить буфер
// с помощью процедуры ::free!
char * GetDisplayName( LPCITEMIDLIST pidl, const STRRET& value )
{
char * result = NULL;
switch( value.uType )
{
case STRRET_CSTR:
result = strdup( value.cStr );
break;
case STRRET_WSTR:
{
int iSize = WideCharToMultiByte( CP_THREAD_ACP, 0, value.pOleStr, -1, NULL, 0, NULL, NULL );
result = (char *)malloc(iSize);
WideCharToMultiByte( CP_THREAD_ACP, 0, value.pOleStr, -1, result, iSize, NULL, NULL );
pMalloc->Free( value.pOleStr );
}
break;
case STRRET_OFFSET:
result = strdup( PSTR( PBYTE(pidl) + value.uOffset ) );
break;
}
return result;
}
int Level = 0;
// Основная процедура выполняет рекурсивный просмотр структуры папок, начиная с данной.
// Она выводит имена элементов, попутно отслеживая отступы.
void ShowFolder( LPSHELLFOLDER folder )
{
LPITEMIDLIST pidlChild;
STRRET Value;
LPENUMIDLIST Iterator;
ULONG celtFetched;
LPSHELLFOLDER child;
hr = folder->EnumObjects( NULL /* no owner window */, SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN, &Iterator );
if( SUCCEEDED( hr ) )
__try
{
Level++;
for(;;)
{
hr = Iterator->Next( 1, &pidlChild, &celtFetched );
if( hr != NOERROR ) break;
__try
{
hr = folder->GetDisplayNameOf( pidlChild, SHGDN_INFOLDER | SHGDN_INCLUDE_NONFILESYS, &Value );
if( SUCCEEDED( hr ) )
{
char * s = GetDisplayName( pidlChild, Value );
WriteChar( ' ', Level );
WritelnStr( s );
free( reinterpret_cast<void *>( s ) );
}
hr = folder->BindToObject( pidlChild, NULL, IID_IShellFolder, (void**)&child );
if( SUCCEEDED( hr ) )
__try
{
ShowFolder( child );
}
__finally
{
child->Release();
}
}
__finally
{
pMalloc->Free( pidlChild );
}
}
Level--;
}
__finally
{
Iterator->Release();
}
}
LPSHELLFOLDER desktop = NULL;
ITEMIDLIST itemidlist = { { 0 } };
LPITEMIDLIST pidlItself = & itemidlist; // This pidl now points to an empty Identifier List.
// It is point to owner folder itself.
// NB: It works only for a root folder!
STRRET Value;
// Основное тело программы выполняет необходимую инициализацию, затем
// восстанавливает имя корневой папки, выводит его, и обращается к ShowFolder для выполнения рекурсии.
void main( int argc, char *argv[])
{
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if( SUCCEEDED( hr ) )
__try
{
hr = SHGetMalloc( &pMalloc );
if( SUCCEEDED( hr ) )
__try
{
hr = SHGetDesktopFolder( &desktop );
if( SUCCEEDED( hr ) )
__try
{
hr = desktop->GetDisplayNameOf( pidlItself, SHGDN_NORMAL | SHGDN_INCLUDE_NONFILESYS, &Value );
if( SUCCEEDED( hr ) )
{
WritelnStr( GetDisplayName( pidlItself, Value ) );
}
ShowFolder( desktop );
}
__finally
{
desktop->Release();
}
}
__finally
{
pMalloc->Release();
}
}
__finally
{
CoUninitialize();
}
} |
// Delphi
program ShellA;
// Это консольное приложение печатает на стандартный вывод дерево папок
// основного пространства имён оболочки Windows.
uses
Windows, ActiveX, ShlObj;
{$APPTYPE CONSOLE}
// Эта процедура производит вывод строки, попутно конвертируя её из кодировки ANSI в кодировку OEM.
procedure WriteStr(const s: string);
begin
CharToOemBuff( PChar(s), PChar(s), Length(s) );
Write(s);
end;
// Эта процедура производит вывод указанного количества символов, попутно конвертируя их из кодировки ANSI в кодировку OEM.
procedure WriteChars(Ch: char; Count: Integer);
var
s: string;
begin
CharToOemBuff( @Ch, @Ch, 1 );
s := StringOfChar(Ch, Count);
Write( s );
end;
// Эта процедура производит вывод строки, попутно конвертируя её из кодировки ANSI в кодировку OEM.
// Эта процедура также добавляет перевод каретки по окончании вывода.
procedure WritelnStr(const s: string);
begin
WriteStr( s + #13#10 );
end;
var
pMalloc: IMalloc;
hr: HRESULT;
// Эта процедура интерпретирует содержание структуры STRRET.
// Также она освобождает временные буфера при необходимости, так что
// для её корректной работы требуется наличие объекта pMalloc.
function GetDisplayName( pidl: PItemIDList; const Value: STRRET ): string;
begin
with Value do
case uType of
STRRET_CSTR: Result := PChar(@cStr[0]);
STRRET_WSTR:
begin
Result := pOleStr;
pMalloc.Free( pOleStr );
end;
STRRET_OFFSET: Result := PChar( LongWord(pidl) + uOffset );
end;
end;
var
Level: Integer;
// Основная процедура выполняет рекурсивный просмотр структуры папок, начиная с данной.
// Она выводит имена элементов, попутно отслеживая отступы.
procedure ShowFolder(folder: IShellFolder);
var
pidlChild: PItemIDList;
Value: STRRET;
Iterator: IEnumIDList;
celtFetched: ULONG;
child: IShellFolder;
begin
hr := folder.EnumObjects( 0 (* no owner window *), SHCONTF_FOLDERS or SHCONTF_INCLUDEHIDDEN, Iterator );
if Succeeded( hr ) then
try
Inc(Level);
while true do
begin
hr := Iterator.Next( 1, pidlChild, celtFetched );
if hr <> NOERROR then Break;
try
hr := folder.GetDisplayNameOf( pidlChild, SHGDN_INFOLDER or SHGDN_INCLUDE_NONFILESYS, Value );
if Succeeded( hr ) then
begin
WriteChars( ' ', Level );
WritelnStr( GetDisplayName( pidlChild, Value ) );
end;
hr := folder.BindToObject( pidlChild, nil, IID_IShellFolder, Pointer(child) );
if Succeeded( hr ) then
try
ShowFolder( child );
finally
child := nil;
end;
finally
pMalloc.Free( pidlChild );
end;
end;
Dec(Level);
finally
Iterator := nil;
end;
end;
var
desktop: IShellFolder;
mkid: SHITEMID;
pidlItself: PItemIDList;
Value: STRRET;
// Основное тело программы выполняет необходимую инициализацию, затем
// восстанавливает имя корневой папки, выводит его, и обращается к ShowFolder для выполнения рекурсии.
begin
Level := 0;
mkid.cb := 0;
pidlItself := @mkid; // This pidl now points to an empty Identifier List. It is points to owner folder itself.
// NB: It works only for a root folder!
hr := CoInitializeEx( nil, COINIT_MULTITHREADED );
if Succeeded( hr ) then
try
hr := SHGetMalloc( pMalloc );
if Succeeded( hr ) then
try
hr := SHGetDesktopFolder( desktop );
if Succeeded( hr ) then
try
hr := desktop.GetDisplayNameOf( pidlItself, SHGDN_NORMAL or SHGDN_INCLUDE_NONFILESYS, Value );
if Succeeded( hr ) then
begin
WritelnStr( GetDisplayName( pidlItself, Value ) );
end;
ShowFolder( desktop );
finally
desktop := nil;
end;
finally
pMalloc := nil;
end;
finally
CoUninitialize;
end;
end. |
Дополнительные возможности
Прежде всего, Ваше приложение всегда можете получить строку
с именем объекта, представленном в удобном для Вас формате.
Для этого интерфейс IShellFolder предоставляет метод
GetDisplayNameOf.
Вы можете указать один из следующих требующихся форматов:
SHGDN_NORMAL |
SHGDN_INFOLDER |
SHGDN_INCLUDE_NONFILESYS |
SHGDN_FORADDRESSBAR |
SHGDN_FORPARSING |
Имя элемента, полученное с установленным флагом
SHGDN_FORPARSING, имеет особое значение. Вы можете
использовать такое имя как командную строку для запуска
приложения. Говоря точнее - такое имя эквивалентно понятию
пути файловой системы.
Интерфейс IShellFolder предоставляет метод SetNameOf,
позволяющий изменить экранное имя файлового объекта или
вложенной папки. Изменяя экранное имя элемента, Вы изменяете
его идентификатор, поэтому функция возвращает PIDL-указатель
на новый идентификатор. Изменение экранного имени файлового
объекта приводит к его фактическому переименованию в файловой
системе.
Интерфейс IShellFolder также предоставляет метод
ParseDisplayName, который позволяет узнать идентификатор
элемента по его имени. Этому методу необходимо передавать имя,
сгенерированное методом GetDisplayNameOf с установленным
флагом SHGDN_FORPARSING.
С помощью глобального метода SHGetPathFromIDList по списку
идентификаторов, определяющих объект относительно корня
пространства имён, можно определить путь к объекту файловой
системы.
С помощью глобального метода SHAddToRecentDocs Ваше
приложение может добавить документ к списку последних, с
которыми работал пользователь, или очистить этот список.
Поиск интерфейсов, позволяющих оперировать с данным
объектом, можно осуществить через метод GetUIObjectOf
интерфейса IShellFolder (обычно запрашиваются интерфейсы
контекстных меню и операций drag-and-drop).
Частой задачей является выполнение некоторых команд
контекстного меню для данного элемента. Например, для открытия
окна свойств объекта нужно запросить интерфейс IContextMenu
этого объекта, и активизировать команду "Properties". Ниже
представлен пример:
// C++
#define _WIN32_DCOM
#include <objbase.h>
#include <shlobj.h>
// Это приложение выводит на экран
// окно свойств "Мой компьютер".
HRESULT hr;
LPMALLOC pMalloc = NULL;
LPSHELLFOLDER desktop = NULL;
LPCONTEXTMENU mnu = NULL;
LPITEMIDLIST pidlDrives = NULL;
CMINVOKECOMMANDINFO cmd;
void main( int argc, char *argv[])
{
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if( SUCCEEDED( hr ) )
__try
{
hr = SHGetMalloc( &pMalloc );
if( SUCCEEDED( hr ) )
__try
{
hr = SHGetDesktopFolder( &desktop );
if( SUCCEEDED( hr ) )
__try
{
hr = SHGetSpecialFolderLocation( NULL, CSIDL_DRIVES, &pidlDrives );
if( SUCCEEDED( hr ) )
__try
{
hr = desktop->GetUIObjectOf( NULL, 1, const_cast<LPCITEMIDLIST*>(&pidlDrives), IID_IContextMenu, NULL, (void**) &mnu );
if( SUCCEEDED( hr ) )
__try
{
memset( & cmd, 0, sizeof( cmd ) );
cmd.cbSize = sizeof( cmd );
cmd.fMask = 0;
cmd.hwnd = 0;
cmd.lpVerb = "Properties";
cmd.nShow = SW_SHOWNORMAL;
hr = mnu->InvokeCommand( & cmd );
}
__finally
{
mnu->Release();
}
}
__finally
{
pMalloc->Free( pidlDrives );
}
}
__finally
{
desktop->Release();
}
}
__finally
{
pMalloc->Release();
}
}
__finally
{
CoUninitialize();
}
} |
// Delphi
program ShellB;
// Это приложение выводит на экран
// окно свойств "Мой компьютер".
uses
Windows,
ActiveX,
ShlObj;
var
pMalloc: IMalloc;
desktop: IShellFolder;
mnu: IContextMenu;
hr: HRESULT;
pidlDrives: PItemIDList;
cmd: TCMInvokeCommandInfo;
begin
hr := CoInitializeEx( nil, COINIT_MULTITHREADED );
if SUCCEEDED( hr ) then
try
hr := SHGetMalloc( pMalloc );
if SUCCEEDED( hr ) then
try
hr := SHGetDesktopFolder( desktop );
if SUCCEEDED( hr ) then
try
hr := SHGetSpecialFolderLocation( 0, CSIDL_DRIVES, pidlDrives );
if SUCCEEDED( hr ) then
try
hr := desktop.GetUIObjectOf( 0, 1, pidlDrives, IContextMenu, nil, Pointer(mnu) );
if SUCCEEDED( hr ) then
try
FillMemory( @cmd, sizeof(cmd), 0 );
with cmd do
begin
cbSize := sizeof( cmd );
fMask := 0;
hwnd := 0;
lpVerb := PChar( 'Properties' );
nShow := SW_SHOWNORMAL;
end;
hr := mnu.InvokeCommand( cmd );
finally
mnu := nil;
end;
finally
pMalloc.Free( pidlDrives );
end;
finally
desktop := nil;
end;
finally
pMalloc := nil;
end;
finally
CoUninitialize;
end;
end. |
На этом в обзоре мы ставим точку. В дальнейшем я постараюсь
отдельным обзором охватить тему "Расширения оболочки", где
обязательно коснусь и расширений пространства имён оболочки.
Заглядывайте изредка на мой уголок
разработчика.
Copyright 1999 by Akzhan Abdulin. При
публикации просьба указывать источник и авторство.
Комментарии, исправления, замечания и пожелания
приветствуются по адресу: akzhan@info.khv.ru.
|