Getting a Folder's ID

Before you can make use of a namespace object, you need a way to identify it. This means obtaining either its pointer to an item identifier list (PIDL) or, in the case of file system objects, its path. This section discusses two of the simpler ways to obtain object IDs.

For a more powerful approach that will work with any folder, use the IShellFolder interface. See Getting Information About the Contents of a Folder for more details.

The OpenFiles Dialog Box

To enable the user to navigate the namespace and select a folder, your application can use the IFileDialog interface. Calling this interface with the FOS_PICKFOLDERS flag launches the Open Files common dialog box in "pick folders" mode.

For Windows Vista and later, this is the recommended way to pick folders.

The SHBrowseForFolder Dialog Box

To enable the user to navigate the namespace and select a folder, your application can simply invoke SHBrowseForFolder. Calling this function launches a dialog box with a UI that works somewhat like the Open or SaveAs common dialog boxes.

When the user selects a folder, SHBrowseForFolder returns the folder's fully qualified PIDL and its display name. If the folder is in the file system, the application can convert the PIDL to a path by calling SHGetPathFromIDList. The application can also restrict the range of folders that the user can select from by specifying a root folder. Only folders that are below that root in the namespace will appear. The following illustration shows the SHBrowseForFolder dialog box, with the root folder set to Program Files.

screen shot of the browse for folder dialog box

A simple example of how to use SHBrowseForFolder is provided later.

Special Folders and CSIDLs

A number of commonly used folders are designated as special by the system. These folders have a well-defined purpose, and most of them are present on all systems. Even if they are not present initially, their names and locations are still defined, so they can be added later. The collection of special folders includes all of the system's standard virtual folders, such as Printers, My Documents, and Network Neighborhood. It also includes a number of standard file system folders, such as Program Files and System.

Even though the folders are a standard component of all systems, their names and locations in the namespace can vary. For example, the System directory is C:\Winnt\System32 on some systems and C:\Windows\System32 on others. In the past, environment variables provided a way to determine the name and location of a special folder on any particular system. The Shell now provides a more robust and flexible way to identify special folders, CSIDLs. You should generally use them instead of environment variables.

CSIDLs provide a uniform way of identifying and locating special folders, regardless of their name or location on a particular system. Unlike environment variables, CSIDLs can be used with virtual folders as well as file system folders. Each special folder has a unique CSIDL assigned to it. For example, the Program Files file system folder has a CSIDL of CSIDL_PROGRAM_FILES, and the Network Neighborhood virtual folder has a CSIDL of CSIDL_NETWORK.

A CSIDL is used in conjunction with one of several Shell functions to retrieve a special folder's PIDL, or a special file system folder's path. If the folder does not exist on a system, your application can force it to be created by combining its CSIDL with CSIDL_FLAG_CREATE. The CSIDL can be passed to the following functions:

Note that these two functions were introduced with version 5.0 of the Shell and supersede the SHGetSpecialFolderLocation and SHGetSpecialFolderPath functions.

A Simple Example of How to Use CSIDLs and SHBrowseForFolder

The following sample function, PidlBrowse, illustrates how to use CSIDLs to retrieve a folder's PIDL, and use SHBrowseForFolder to have the user select a folder. It returns the PIDL and display name of the selected folder.

LPITEMIDLIST PidlBrowse(HWND hwnd, int nCSIDL, LPSTR pszDisplayName)
{
    LPITEMIDLIST pidlRoot = NULL;
    LPITEMIDLIST pidlSelected = NULL;
    BROWSEINFO bi = {0};

    if(nCSIDL)
    {
        SHGetFolderLocation(hwnd, nCSIDL, NULL, NULL, &pidlRoot);
    }

    else
    {
        pidlRoot = NULL;
    }

    bi.hwndOwner = hwnd;
    bi.pidlRoot = pidlRoot;
    bi.pszDisplayName = pszDisplayName;
    bi.lpszTitle = "Choose a folder";
    bi.ulFlags = 0;
    bi.lpfn = NULL;
    bi.lParam = 0;

    pidlSelected = SHBrowseForFolder(&bi);

    if(pidlRoot)
    {
        CoTaskMemFree(pidlRoot);
    }

    return pidlSelected;
}

The calling application passes in a window handle, which is needed by SHBrowseForFolder. The nCSIDL parameter is an optional CSIDL that is used to specify a root folder. Only folders below the root folder in the hierarchy will be displayed. The illustration shown earlier was generated by calling this function with nCSIDL set to CSIDL_PROGRAM_FILES. The calling application also passes in a string buffer, pszDisplayName, to hold the display name of the selected folder when PidlBrowse returns. It is the responsibility of the calling application to free the IDList returned by SHBrowseForFolder using CoTaskMemFree.