Mega Code Archive

 
Categories / Delphi / System
 

Working with objects in the Windows namespace

Title: Working with objects in the Windows' namespace. Question: The W9x/NT4/2000 shell introduced a new way to identify and work with objects, beyond the file system Answer: With Windows 95 and its new shell were introduced some objects that have no counterpart in the file system (i.e the printer folder and its objects). These objects cannot be identified by their path, therefore were introduced the Item Identifier and the Item Identifier List. Every object has a unique item identifier within its container (usually a folder, two object in distinct containers may have identical item identifiers), and an object is uniquely identified within the namespace by the list of item identifiers from the root of the namespace - the Desktop - all the containers of the object, and the object itself. An item identifier has a simple structure (ShlObj.pas): _SHITEMID = record cb: Word; { Size of the ID (including cb itself) } abID: array[0..0] of Byte; { The item ID (variable length) } end; The abID part is variable length and its contents are defined by the shell extension responsible on managing the object. An item identifier list is even simpler (ShlObj.pas): _ITEMIDLIST = record mkid: TSHItemID; end; An item id list is made of item ids one following the other, terminated by an item id whose cb field is set to 0. Item id lists are usually manipulated using a pointers to them, called PIDL (Pointer to Item iDenfifier List). PIDLs mys be allocated and freed using the IMalloc interface, which can be retrieved using SHGetMalloc(). There are two kind of lists, absolute and relative. Absolute lists start from the namespace root, relative lists start from the parent container. The bad news: API function using PIDLs require absolute lists, interface methods (i.e. IShellFolder) require relative lists. Only Windows 2000 API has documented functions to convert PIDLs easily. NT and W9x have a bunch of undocumented functions (look for 'The Secret World of PIDLs' in www.delphizine.com). Here are some Delphi function to manipulate PIDLs. They are tha Pascal translation of functions in a Microsoft sample C program, EnumDesk: // Allocates a new PIDL of cbSize bytes, and initialize all fields to 0 function CreatePidl(cbSize: UINT): PItemIDList; var lpMalloc: IMalloc; begin if SHGetMalloc(lpMalloc) NOERROR then begin Result := nil; Exit; end; Result := lpMAlloc.Alloc(cbSize); if Result nil then FillChar(Result^, cbSize, #0); end; // Returns the Next PIDL in PIDL list function NextPidl(Pidl: PItemIDList): PItemIDList; var lpMem: LPSTR; begin lpMem := LPSTR(Pidl); lpMem := lpMem + Pidl.mkid.cb; Result := PItemIDList(lpMem); end; // Returns PIDL size (a list may contain one or more IDLs) function GetPidlSize(Pidl: PItemIDList): UINT; begin Result := 0; if Pidl nil then begin Result := SizeOf(Pidl.mkid.cb); while Pidl.mkid.cb 0 do begin Result := Result + Pidl.mkid.cb; Pidl := NextPidl(Pidl); end; end; end; // Concatenates Pidl1 and Pidl2 - can be used to build an absolute //list from relative ones function ConcatPidls(Pidl1, Pidl2: PItemIDList): PItemIDList; var cb1, cb2: UINT; begin if Pidl1 nil then // Pidl1 may be nil... cb1 := GetPidlSize(Pidl1) - SizeOf(Pidl1.mkid.cb) else cb1 := 0; cb2 := GetPidlSize(Pidl2); Result := CreatePidl(cb1 + cb2); if Result nil then begin if Pidl1 nil then Move(Pidl1^, Result^, cb1); Move(Pidl2^, (LPSTR(Result)+ cb1)^, cb2); end; end; // Returns a new PIDL which is a copy of the passed PIDL, using the // task allocator function CopyITEMID(Pidl: PItemIDList; MAlloc: IMAlloc): PItemIDList; begin Result := MAlloc.Alloc(Pidl.mkid.cb + SizeOf(Pidl.mkid.cb)); Move(Pidl^, Result^, Pidl.mkid.cb + SizeOf(Pidl.mkid.cb)); end; We can retrieve and work on PIDLs using the SHxxxxx functions and the IShellFolder interface. Here are some of the most useful functions (they are declared in ShlObj.pas), look in the Windows SDK help file for details of how they are used: SHGetSpecialFolderLocation() retrieves the PIDLs of a special folder i.e. the Desktop, My Computer and so on. SHGetPathFromIDList() returns the file system path, if exists, of a PIDL. SHChangeNotify() notify the shell that an application performed an action that requires the shell to update itself (i.e. renamed a folder). SHGetFileInfo() returns many information about any namespace object: icons, type, attributes, display names and more. May be used to retrieve the handle of the system image list. Accepts both PIDLs and paths. SHAddToRecentDocs() adds a document to the shell's list of recently used documents or clears all documents from the list. Accepts both PIDLs and paths. SHBrowseForFolder() displays a dialog box that enables the user to select a shell folder, and returns its PIDL.