Enumerating Items in the Shell

When an application has the IShellFolder interface for a folder, it can determine the folder's contents by using the EnumObjects member function. EnumObjects creates an item enumeration object, which is a set of item identifiers that can be retrieved by using the IEnumIDList interface.

After you get an item enumeration object, your application can retrieve all the item IDs one or more at a time from the enumeration object by repeatedly calling the IEnumIDList::Next member function. Using other member functions, you can skip items in the sequence, return to the beginning of the sequence, or “clone” the enumeration object to save its state. When your application has finished using the enumeration object, you must free the object by calling its Release member function.

The MFCENUM and ENUMDESK samples use a tree view control and a list view control to display items in the shell name space. These two controls are created when the application starts. The desktop folder is retrieved, and the tree view control is filled with the contents of the desktop.

void CMfcenumView::OnFill ()
{
LPSHELLFOLDER lpsf = NULL;
LPITEMIDLIST lpi = NULL;
HRESULT hr;
TV_SORTCB tvscb;

// Get a pointer to the desktop folder.
hr = SHGetDesktopFolder (&lpsf);

if (SUCCEEDED (hr))
{
// Initialize the tree view control to be empty.
m_TreeCtl.DeleteAllItems ();

// Fill in the tree view control from the root.
FillTreeView (lpsf, NULL, TVI_ROOT);

// Release the folder pointer.
lpsf->Release ();
}

tvscb.hParent = TVI_ROOT;
tvscb.lParam = 0;
tvscb.lpfnCompare = TreeViewCompareProc;

// Sort the items in the tree view control.
m_TreeCtl.SortChildrenCB (&tvscb, FALSE);
}

The application-defined FillTreeView function enumerates the items in the folder identified by the first parameter (a pointer to a shell folder). The second parameter is the fully qualified item ID list to the item (the PIDL of the item identified by the first parameter). The third parameter is the tree view parent item. This function will add only items that are folders or that have subfolders.

The FillTreeView function first calls SHGetMalloc. All user interface extensions must use the task allocator to allocate or free memory objects (such as item ID lists) returned across shell interfaces. SHGetMalloc does this.

void CMfcenumView::FillTreeView (
LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, HTREEITEM hParent)
{
TV_ITEM tvi; // tree view item
TV_INSERTSTRUCT tvins; // tree view insert structure
HTREEITEM hPrev = NULL; // previous item added
LPSHELLFOLDER lpsf2 = NULL;
LPENUMIDLIST lpe = NULL;
LPITEMIDLIST lpi = NULL, lpiTemp = NULL, lpifqThisItem;
LPTVITEMDATA lptvid = NULL;
LPMALLOC lpMalloc = NULL;
ULONG ulFetched;
UINT uCount = 0;
HRESULT hr;
char szBuff [256];
HWND hwnd = ::GetParent (m_TreeCtl.m_hWnd);

// Allocate a shell memory object.
hr = ::SHGetMalloc (&lpMalloc);
if (FAILED (hr))
return;

if (SUCCEEDED (hr))
{
// Get the IEnumIDList object for the given folder.
hr = lpsf->EnumObjects (hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
&lpe);

if (SUCCEEDED (hr))
{
// Enumerate through the list of folder and nonfolder objects.
while (S_OK == lpe->Next (1, &lpi, &ulFetched))
{
// Create a fully qualified path to the current item.
// The SH* functions take a fully qualified path PIDL,
// while the interface member functions take a
// relative path PIDL.
ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;

// Determine what type of object you have.
lpsf->GetAttributesOf (
1, (const struct _ITEMIDLIST **)&lpi, &ulAttrs);

if (ulAttrs & (SFGAO_HASSUBFOLDER | SFGAO_FOLDER))
{
// You need this next if statement to
// avoid adding objects that are not real
// folders to the tree. Some objects can
// have subfolders but aren't real folders.
if (ulAttrs & SFGAO_FOLDER)
{
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
TVIF_PARAM;

if (ulAttrs & SFGAO_HASSUBFOLDER)
{
// This item has subfolders, so put a plus sign in the
// tree view control. The first time the user clicks
// the item, you should populate the subfolders.
tvi.cChildren = 1;
tvi.mask |= TVIF_CHILDREN;
}

// Get some memory for the ITEMDATA structure.
lptvid = (LPTVITEMDATA) lpMalloc->Alloc (
sizeof (TVITEMDATA));
if (! lptvid)
goto Done; // Error - could not allocate memory

// Now get the friendly name to
// put in the tree view control.
if (! GetName (lpsf, lpi, SHGDN_NORMAL, szBuff))
goto Done; // Error - could not get friendly name

tvi.pszText = szBuff;
tvi.cchTextMax = MAX_PATH;

lpifqThisItem = ConcatPidls (lpifq, lpi);

// Now make a copy of the ITEMIDLIST.
lptvid->lpi = CopyITEMID (lpMalloc, lpi);

GetNormalAndSelectedIcons (lpifqThisItem, &tvi);

lptvid->lpsfParent = lpsf; // pointer to parent folder
lpsf->AddRef ();

lptvid->lpifq = ConcatPidls (lpifq, lpi);
tvi.lParam = (LPARAM)lptvid;

// Populate the tree view insert structure.
// The item is the one filled above.
// Insert it after the last item inserted at this level,
// and indicate that this is a root entry.
tvins.item = tvi;
tvins.hInsertAfter = hPrev;
tvins.hParent = hParent;

// Add the item to the tree.
hPrev = m_TreeCtl.InsertItem (&tvins);
}

// Free the task allocator for this item.
lpMalloc->Free (lpifqThisItem);
lpifqThisItem = 0;
}

lpMalloc->Free (lpi); // free the PIDL the shell gave you
lpi = 0;
}
}
}

else
return;

Done:
if (lpe)
lpe->Release ();

// The following two if statements will be TRUE only if you got here
// on an error condition from the goto statement. Otherwise, free
// this memory at the end of the while loop above.
if (lpi && lpMalloc)
lpMalloc->Free (lpi);
if (lpifqThisItem && lpMalloc)
lpMalloc->Free (lpifqThisItem);

if (lpMalloc)
lpMalloc->Release ();
}

FillTreeView used the folder's IShellFolder interface to get the folder's contents and then used the IShellFolder::EnumObjects member function to create an item enumeration object. This function then called the IEnumIDList::Next member function to iterate through all the item IDs for all the folders in the name space.