How to Find Shell (System) Folders in Win32 from C/C++

It’s possible to refer to e.g. My Documents folder on your own computer by the path you know, and it’ll work. But if you want to distribute your program among users, you cannot expect the fixed path to work. E.g. Windows allow you to save user’s profile folders anywhere on the disk you want; it’s not a rule that all are saved under Documents and Settings; e.g. mine are saved under folder called Profiles, since I wanted to preserve the original Documents and Settings from my previous Windows installation. Also, Windows need not to be installed under C:\Windows; mine are under C:\Windows.1.

But, there is a (quite) simple way to find all the necessary system folders in Windows. All you have to do is be a bit careful.

There is a registry key HKEY_CURRENT_USER\ Software\ Microsoft\ Windows\ CurrentVersion\ Explorer\ Shell Folders. This key contains several values that indicate where various special folders are kept on the user’s hard drive.

But! When the operating system is first installed, it does not add these values to this registry key. These values are added when any application calls the SHGetSpecialFolderLocation() function for the very first time. Also, this function uses this registry value in an algorithm to determine the user’s personal directory, and this algorithm is likely to change in future versions of Windows. Because of this, you should never access this registry key directly.

The Win32 shlobj.h header file prototypes the SHGetSpecialFolderLocation() function and defines some symbols for use with this function. The function prototype is shown here, and the symbols can be found in the Win32 SDK documentation.

HRESULT SHGetSpecialFolderLocation(HWND hwndOwner, int nFolder, LPITEMIDLIST* ppidl);

The shell and its functions all work with item IDs, lists of item IDs, and pointers to lists of item IDs. When you call SHGetSpecialFolderLocation(), you pass a special folder symbol in the nFolder parameter. E.g. to get the user’s personal directory, pass CLSID_PERSONAL for this parameter. The function then allocates a block of memory that contains a contiguous list of item IDs. Each item ID is a binary representation of an item in the shell’s namespace. The root of the shell’s namespace is the Desktop, and a list of item IDs is the binary representation of an item’s location in the namespace starting from the Desktop. SHGetSpecialFolderLocation()‘s last parameter, ppidl, is a pointer to an LPITEMIDLIST variable that you must allocate (you just allocate the pointer, not any additional memory). This pointer’s value is set to point to the block of memory allocated for the special folder’s item ID list just before the SHGetSpecialFolderLocation() function returns.

Then, you must convert an item ID list to a fully qualified path name that the file system understands. Calling SHGetPathFromIDList() does the trick:

BOOL SHGetPathFromIDList(LPCITEMIDLIST pidl, LPSTR pszPath);

The pidl parameter is a pointer to an ID list (returned from SHGetSpecialFolderLocation()), and the pszPath parameter identifies a character buffer that gets the folder’s path name. The path’s buffer must be at least _MAX_PATH characters long.

The last step, which is easy to forget, is to free the item ID list. The SHGetSpecialFolderLocation() function allocates memory for the item ID list by using the calling process’s task allocator. You are responsible for freeing this memory by using the task’s allocator. You get the task’s allocator by calling SHGetMalloc():

HRESULT SHGetMalloc(LPMALLOC * ppMalloc);

This function returns a pointer to an IMalloc interface that you can use to free the item ID list. Following code demonstrates all the steps necessary to get the full path of a special folder:

// Allocate a pointer to an Item ID list
LPITEMIDLIST pidl;

// Get a pointer to an item ID list that represents the path of a special folder
HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);

// Convert the item ID list's binary representation into a file system path
char szPath[_MAX_PATH];
BOOL f = SHGetPathFromIDList(pidl, szPath);

// Allocate a pointer to an IMalloc interface
LPMALLOC pMalloc;

// Get the address of our task allocator's IMalloc interface
hr = SHGetMalloc(&pMalloc);

// Free the item ID list allocated by SHGetSpecialFolderLocation
pMalloc->Free(pidl);

// Free our task allocator
pMalloc->Release();

// Work with the special folder's path (contained in szPath)...

And that’s it! Good luck!

5 responses to “How to Find Shell (System) Folders in Win32 from C/C++

  1. I include shlobj.h in my project ans I have a lot of “error C4430: missing type specifier – int assumed. Note: C++ does not support” in commctrl.h and shlguid.h.
    Have you face that problem before?

  2. I used the above code for VS6 and VS 2003, so I didn’t get this error, but as I understand from this page, it has to do with being C-header, instead of C++-conformal, where implicit int is not supported.

    So, you have 2 options – use extern “C” {} around the header, or (as suggested in above link) use #pragma warning (disable: 4430) before including the header.

    Hope it helps!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s