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!