Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.
Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...
В операционных системах компании 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++ #includeLPITEMIDLIST 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 usesShlObj; 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++ #includesize_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 usesWindows, 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 functionHRESULT_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 и SHGetSpecialFolderPath принимают в качестве параметра одно из ниже указанных значений:
Необходимо отметить, что эти функции недавно были выделены в отдельную библиотеку - shfolder.dll.
Функция SHBrowseForFolder позволяет ограничить видимое пространство имён, задав в поле pidlRoot корневую папку, с которой будет идти просмотр (по умолчанию - "Рабочий стол"), и указав типы объектов, которые приемлемы в качестве выбранных, в поле ulFlags.
Описание флагов, которые допустимы в поле ulFlags: BIF_BROWSEFORCOMPUTER В качестве выбора допустимы только компьютеры.
Ниже представлен пример работы с функцией SHBrowseForFolder на Delphi. Очень часто приложение использует функцию обратного вызова, чтобы указать исходную папку для просмотра. Здесь мы будем использовать механизм обратного вызова именно для этой цели, а та кже для того, чтобы установить некий текст при инициализации диалога:
// Delphi (* * Эта программа показывает на экране диалог выбора папки, * где корнем дерева им¸н служит папка "Мой компьютер", а текущим * каталогом - папка "Файлы программ". *) programShellD; 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 beginResult := 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. functionGetProgramFilesDir: string
; const
DefaultProgramFilesDir = '%SystemDrive%Program Files'; var
FolderName: string
; dwStrSize: DWORD; begin
if
Win32Platform = VER_PLATFORM_WIN32_NT then
begin
FolderName := GetProgramFilesDirByKeyStr('SoftwareMicrosoftWindows NTCurrentVersion'); end
; if
Length(FolderName) = 0 then
begin
FolderName := GetProgramFilesDirByKeyStr('SoftwareMicrosoftWindowsCurrentVersion'); 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 ifSUCCEEDED( 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 ); ifAssigned(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
.
В вышеприведённом коде есть некоторые моменты, которые будут разьяснены ниже.
Навигация по пространству имён
Каждый объект-папка прдоставляет Вам возможность перебора всех объектов, которыми данный объект владеет. Для этого Вам предоставляется метод EnumObjects интерфейса IShellFolder, который возвращает интерфейс-итератор IEnumIDList. При этом Вы можете огранич ить список (включать папки, не папки, скрытые и системные объекты).
Описание методов интерфейса IEnumIDList:
Таким образом Вы сможете получить набор указателей на списки идентификаторов, причём эти списки будут относительными по отношению к папке-владельцу.
Чтобы получить интерфейс IShellFolder для любого из этих объектов, Вам потребуется осуществить привязку, вызвав метод BindToObject интерфейса IShellFolder папки-владельца.
Чтобы узнать атрибуты данного объекта или нескольких объектов, необходимо вызвать метод GetAttributesOf интерфейса IShellFolder папки-владельца. При этом перед вызовом этого метода необходимо установить те атрибуты, значения которых Вы бы хотели выяснить. Если запрошены атрибуты нескольких элементов, то метод вернёт только те значения атрибутов, которые совпадают у всех переданных элементов. В частности, Вы сможете взять интерфейс IShellFolder только от тех объектов, которые имеют атрибут SFGAO_FOLDER. Вы можете обновить информацию об элементах, входящих в папку, использовав флаг SFGAO_VALIDATE. Ниже представлен пример навигации по основному пространству имён:
// C++ with Microsoft extensions // Это консольное приложение печатает на стандартный вывод дерево папок // основного пространства имён оболочки Windows. #define _WIN32_DCOM #include#include #include #include using namespace std; // Эта процедура производит вывод строки, попутно // конвертируя её из кодировки ANSI в кодировку OEM. void WriteStr(const char * s) { char * s1 = strdup( s ); CharToOemBuff( s1, s1, strlen(s1) ); cout << s1; free( reinterpret_cast ( 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) { WriteStr( s ); cout << " "; } 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 ( 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 ) ) { char * sTempName = GetDisplayName( pidlItself, Value ); WritelnStr( sTempName ); free( reinterpret_cast (sTempName) ); } ShowFolder( desktop ); } __finally { desktop->Release(); } } __finally { pMalloc->Release(); } } __finally { CoUninitialize(); } }
// Delphi programShellA;
// Это консольное приложение печатает на стандартный вывод дерево папок // основного пространства им¸н оболочки Windows. usesWindows, ActiveX, ShlObj;
{$APPTYPE CONSOLE} // Эта процедура производит вывод строки, попутно // конвертируя е¸ из кодировки ANSI в кодировку OEM. procedureWriteStr(s: string
); begin
UniqueString( s ); CharToOemBuff( PChar(s), PChar(s), Length(s) ); Write(s); end
;
// Эта процедура производит вывод указанного // количества символов, попутно конвертируя их из кодировки ANSI // в кодировку OEM. procedureWriteChars(Ch: char; Count: Integer); var
s: string
; begin
CharToOemBuff( @Ch, @Ch, 1 ); s := StringOfChar(Ch, Count); Write( s ); end
;
// Эта процедура производит вывод строки, попутно // конвертируя е¸ из кодировки ANSI в кодировку OEM. // Эта процедура также добавляет перевод каретки по окончании вывода. procedureWritelnStr(const
s: string
); begin
WriteStr( s + #13#10 ); end
; var
pMalloc: IMalloc; hr: HRESULT;
// Эта процедура интерпретирует содержание структуры STRRET. // Также она освобождает временные буфера при необходимости, так что // для е¸ корректной работы требуется наличие объекта pMalloc. functionGetDisplayName( 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;
// Основная процедура выполняет рекурсивный просмотр структуры папок, начиная с данной. // Она выводит имена элементов, попутно отслеживая отступы. procedureShowFolder(folder: IShellFolder); var
pidlChild: PItemIDList; Value: STRRET; Iterator: IEnumIDList; celtFetched: ULONG; child: IShellFolder; begin
hr := folder.EnumObjects( 0
(* no owner window *), SHCONTF_FOLDERS orSHCONTF_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 для выполнения рекурсии. beginLevel := 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_FORPARSING, имеет особое значение. Вы можете использовать такое имя как командную строку для запуска приложения. Говоря точнее - такое имя эквивалентно понятию пути файловой системы.
Интерфейс IShellFolder предоставляет метод SetNameOf, позволяющий изменить экранное имя файлового объекта или вложенной папки. Изменяя экранное имя элемента, Вы изменяете его идентификатор, поэтому функция возвращает PIDL-указатель на новый идентификатор. Изменение экранного имени файлового объекта приводит к его фактическому переименованию в файловой системе.
Интерфейс IShellFolder также предоставляет метод ParseDisplayName, который позволяет узнать идентификатор элемента по его имени. Этому методу необходимо передавать имя, сгенерированное методом GetDisplayNameOf с установленным флагом SHGDN_FORPARSING.
С помощью глобального метода SHGetPathFromIDList по списку идентификаторов, определяющих объект относительно корня пространства имён, можно определить путь к объекту файловой системы.
С помощью глобального метода SHAddToRecentDocs Ваше приложение может добавить документ к списку последних, с которыми работал пользователь, или очистить этот список.
С помощью глобального метода SHEmptyRecycleBin, появившегося в версии 4.71 оболочки Windows, Ваше приложение может очистить корзину (Recycle Bin). Удаление файла в корзину (то есть - с возможностью дальнейшего восстановления) производится глобальным метод ом SHFileOperation, подробное описание которого выходит за рамки этого обзора. Вы также можете узнать количество объектов, расположенных в корзине, и их суммарный размер, с помощью метода SHQueryRecycleBin.
// C/C++ // Проверка и очистка корзины на локальном диске C: SHQueryRBInfo.cbSize = sizeof(SHQUERYRBINFO); hr = SHQueryRecycleBin("C:\", &SHQueryRBInfo); if( SUCCEEDED(hr) && (SHQueryRBInfo.i64NumItems > 0) ) { SHEmptyRecycleBin( 0, "C:\", 0 ); )
// Delphi // Данные типы и методы не описаны в ShlObj.Pas, // поэтому опишем их сами. typeSHQUERYRBINFO = record
cbSize: DWORD; i64Size: Int64; i64NumItems: Int64; end
; LPSHQUERYRBINFO = ^SHQUERYRBINFO; TSHQueryRBInfo = SHQUERYRBINFO; const
shell32 = 'shell32.dll'; function
SHQueryRecycleBinA(pszRootPath: PAnsiChar; var
pSHQueryRBInfo: TSHQueryRBInfo): HResult; stdcall
; external
shell32 name 'SHQueryRecycleBinA'; function
SHQueryRecycleBinW(pszRootPath: PWideChar; var
pSHQueryRBInfo: TSHQueryRBInfo): HResult; stdcall
; external
shell32 name 'SHQueryRecycleBinW'; function
SHQueryRecycleBin(pszRootPath: PChar; var
pSHQueryRBInfo: TSHQueryRBInfo): HResult; stdcall
; external
shell32 name 'SHQueryRecycleBinA'; function
SHEmptyRecycleBinA(hWnd: HWND; pszRootPath: PAnsiChar; dwFlags: DWORD): HResult; stdcall
; external
shell32 name 'SHEmptyRecycleBinA'; function
SHEmptyRecycleBinW(hWnd: HWND; pszRootPath: PWideChar; dwFlags: DWORD): HResult; stdcall
; external
shell32 name 'SHEmptyRecycleBinW'; function
SHEmptyRecycleBin(hWnd: HWND; pszRootPath: PChar; dwFlags: DWORD): HResult; stdcall
; external
shell32 name 'SHEmptyRecycleBinA';
// Проверка и очистка корзины на локальном диске C: SHQueryRBInfo.cbSize := sizeof(TSHQueryRBInfo); hr := SHQueryRecycleBin(PChar('C:'), SHQueryRBInfo); ifSucceeded(hr) and
(SHQueryRBInfo.i64NumItems > 0) then
begin
SHEmptyRecycleBin( Self.Handle, PChar('C:'), 0 ); end
;
Поиск интерфейсов, позволяющих оперировать с данным объектом, можно осуществить через метод GetUIObjectOf интерфейса IShellFolder (обычно запрашиваются интерфейсы контекстных меню и операций drag-and-drop).
Частой задачей является выполнение некоторых команд контекстного меню для данного элемента. Например, для открытия окна свойств объекта нужно запросить интерфейс IContextMenu этого объекта, и активизировать команду "Properties". Ниже представлен пример:
/ / C++ #define _WIN32_DCOM #include#include // Это приложение выводит на экран // окно свойств "Мой компьютер". 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 (&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 programShellB;
// Это приложение выводит на экран // окно свойств "Мой компьютер". usesWindows, 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
.
Все примеры в данном обзоре являются реально работающими (предварительно скомпилированными). Для компиляции использовались Microsoft Visual C++ 6.0 SP3 и Borland Delphi 4.0 UP3. К сожалению, при конвертации в HTML могли возникнуть маленькие погрешности, к оторые пока мне неизвестны.