Using PIDLs and Display Names

This section presents an example illustrating how to retrieve the 
location of a special folder, walk an item identifier list, and use the
IShellFolder interface to retrieve display names. The example is a 
console application that prints the display names of the folders a user
would have to open to get to the Programs folder. To display them, the 
application would carry out these steps:
1. Retrieve the PIDL (obtain a pointer to an item identifier list) for 
   the Programs folder by using the SHGetSpecialFolderLocation function.
2. Bind to the desktop folder (retrieve the folder's IShellFolder
   interface) by using the SHGetDesktopFolder function.
3. Walk the item identifier list and process elements as follows: print
   the subfolder's display name, bind to the subfolder, and release the
   parent folder's IShellFolder interface.
 
Before carrying out any of the preceding steps, the application uses 
the SHGetMalloc function to retrieve a pointer to the shell's IMalloc 
interface, which it saves in the following global variable.
// Global pointer to the shell's IMalloc interface.  
LPMALLOC g_pMalloc; 
 
The following example shows the application's main function. This 
function carries out all of the steps described previously, although it
calls the application-defined GetNextItemID and CopyItemID functions to
walk the item identifier list and the application-defined PrintStrRet 
function to print the display names. Code for these application-defined
functions is shown following the code for the main function. 

// main - the application's entrypoint function  
int __cdecl main() 
{ 
    LPITEMIDLIST pidlPrograms; 
    LPSHELLFOLDER pFolder; 
 
    // Get the shell's allocator. 
    if (!SUCCEEDED(SHGetMalloc(&g_pMalloc))) 
        return 1; 
 
    // Get the PIDL for the Programs folder. 
    if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, 
            CSIDL_PROGRAMS, &pidlPrograms))) { 
 
        // Start with the desktop folder. 
        if (SUCCEEDED(SHGetDesktopFolder(&pFolder))) { 
            LPITEMIDLIST pidl; 
 
            // Process each item identifier in the list. 
            for (pidl = pidlPrograms; pidl != NULL; 
                    pidl = GetNextItemID(pidl)) { 
                STRRET sName; 
                LPSHELLFOLDER pSubFolder; 
                LPITEMIDLIST pidlCopy; 
 
                // Copy the item identifier to a list by itself. 
                if ((pidlCopy = CopyItemID(pidl)) == NULL) 
                    break; 
 
                // Display the name of the subfolder. 
                if (SUCCEEDED(pFolder->lpVtbl->GetDisplayNameOf( 
                        pFolder, pidlCopy, SHGDN_INFOLDER, 
                        &sName))) 
                    PrintStrRet(pidlCopy, &sName); 
 
                // Bind to the subfolder. 
                if (!SUCCEEDED(pFolder->lpVtbl->BindToObject( 
                        pFolder, pidlCopy, NULL, 
                        &IID_IShellFolder, &pSubFolder))) { 
                    g_pMalloc->lpVtbl->Free(g_pMalloc, pidlCopy); 
                    break; 
                } 
 
                // Free the copy of the item identifier. 
                g_pMalloc->lpVtbl->Free(g_pMalloc, pidlCopy); 
 
                // Release the parent folder and point to the 
                // subfolder. 
                pFolder->lpVtbl->Release(pFolder); 
                pFolder = pSubFolder; 
            } 
 
            // Release the last folder that was bound to. 
            if (pFolder != NULL) 
                pFolder->lpVtbl->Release(pFolder); 
        } 
 
        // Free the PIDL for the Programs folder. 
        g_pMalloc->lpVtbl->Free(g_pMalloc, pidlPrograms); 
    } 
 
    // Release the shell's allocator. 
    g_pMalloc->lpVtbl->Release(g_pMalloc); 
    return 0; 
} 
 
Following is the GetNextItemID function. Given a pointer to an element 
in an item identifier list, the function returns a pointer to the next 
element (or NULL if there are no more elements). The main function 
calls this function to walk the item identifier list for the Programs 
folder.

// GetNextItemID - points to the next element in an item identifier
//     list.  
// Returns a PIDL if successful or NULL if at the end of the list. 
// pdil - previous element 
LPITEMIDLIST GetNextItemID(LPITEMIDLIST pidl) 
{ 
    // Get the size of the specified item identifier. 
    int cb = pidl->mkid.cb; 
 
    // If the size is zero, it is the end of the list. 
    if (cb == 0) 
        return NULL; 
 
    // Add cb to pidl (casting to increment by bytes). 
    pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb); 
 
    // Return NULL if it is null-terminating or a pidl otherwise. 
    return (pidl->mkid.cb == 0) ? NULL : pidl; 
} 
 
Following is the CopyItemID function. Given a pointer to an element in 
an item identifier list, the function allocates a new list containing 
only the specified element followed by a terminating zero. The main 
function uses this function to create single-element PIDLs, which it 
passes to IShellFolder member functions.

// CopyItemID - creates an item identifier list containing the first  
//     item identifier in the specified list. 
// Returns a PIDL if successful or NULL if out of memory. 
LPITEMIDLIST CopyItemID(LPITEMIDLIST pidl) 
{ 
    // Get the size of the specified item identifier. 
    int cb = pidl->mkid.cb; 
 
    // Allocate a new item identifier list. 
    LPITEMIDLIST pidlNew = (LPITEMIDLIST) 
        g_pMalloc->lpVtbl->Alloc(g_pMalloc, cb + sizeof(USHORT)); 
    if (pidlNew == NULL) 
        return NULL; 
 
    // Copy the specified item identifier. 
    CopyMemory(pidlNew, pidl, cb); 
 
    // Append a terminating zero. 
    *((USHORT *) (((LPBYTE) pidlNew) + cb)) = 0; 
 
    return pidlNew; 
} 
 
The IShellFolder::GetDisplayNameOf member function returns a display 
name in a STRRET structure. The display name may be returned in one of 
three ways, which is specified by the uType member of the STRRET 
structure. The main function calls the following PrintStrRet function 
to print the display name.

// PrintStrRet - prints the contents of a STRRET structure.  
// pidl - PIDL containing the display name if STRRET_OFFSET 
// lpStr - address of the STRRET structure 
void PrintStrRet(LPITEMIDLIST pidl, LPSTRRET lpStr) 
{ 
    LPSTR lpsz; 
    int cch; 
 
    switch (lpStr->uType) { 
 
        case STRRET_WSTR: 
            cch = WideCharToMultiByte(CP_OEMCP, WC_DEFAULTCHAR, 
                lpStr->pOleStr, -1, NULL, 0, NULL, NULL); 
            lpsz = (LPSTR) g_pMalloc->lpVtbl->Alloc(g_pMalloc, cch); 
            if (lpsz != NULL) { 
                WideCharToMultiByte(CP_OEMCP, WC_DEFAULTCHAR, 
                    lpStr->pOleStr, -1, lpsz, cch, NULL, NULL); 
                printf("%s\n", lpsz); 
                g_pMalloc->lpVtbl->Free(g_pMalloc, lpsz); 
            } 
            break; 
 
        case STRRET_OFFSET: 
            printf("%s\n", ((char *) pidl) + lpStr->uOffset); 
            break; 
 
        case STRRET_CSTR: 
            printf("%s\n", lpStr->cStr); 
            break; 
    } 
} 
 
