Since many years, back to Windows 98 as far as I remember, users have special folders in their home directory, called "My Documents", "My Music", "My Pictures" or "My Videos". These were kept relatively untouched up to XP, and their path was retrieved easily: Simply calling the
System.Environment.GetFolderPath() function and passing an enumeration value of
System.Environment.SpecialFolder was enough, since the enumeration contains entries for
MyPictures and so on.
However, the newer folders which exist since Windows Vista are not listed in the enumeration and cannot be retrieved that way. The reason is that the .NET framework was not updated to mirror the changes in the user home directory, and people (including me) started to use hacky and even wrong solutions to get the folder paths to the other folders. This article clears up all the bad solutions and presents the correct way to get the new, "special" folder paths.
Typically, developers quickly used these two wrong solutions to get the path for - let's say - the Download folder.
Wrong Solution #1: Be extremely lazy and get the user home directory root, manually appending the folder name
This is the most horrible solution of the two. Developers started by getting the user home path (in which the special folders are found by default) like this:
string userRoot = System.Environment.GetEnvironmentVariable("USERPROFILE");
This returns something like "C:\Users\Username" in the most cases. In the next step, the folder name was appended, mostly hardcoded, and if you're lucky, it was done with
Path.Combine() instead of a hardcoded backslash:
string downloadFolder = Path.Combine(userRoot, "Downloads");
Good enough, since Windows Vista and newer versions use English folder names on the file system, and the names displayed in Explorer are just virtually localized versions of them, so it should work, should'nt it?
This solution does not work if the user redirected the path of the Downloads folder. It is possible to change the path in the Downloads folder properties and simply choose another location for it. It may not be in the user root directory, it can be anywhere - mine for example are all on the D: partition.
Wrong Solution #2: Use the "Shell Folders" key in the registry
In the next attempt of getting the Downloads folder path, developers searched through the entire registry for a key containing the redirected folder path. Windows must store this information somewhere, right?
Eventually, they landed at "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders". The key looks really promising actually, it contains all the paths to the user folders, even with redirected paths (mine are all on the D partition as you can see):
But, oh wait, it also contains an ominous key with the name "!Do not use this registry key" and the value "Use the
SHGetKnownFolderPath function instead". What the heck does this key want from me? Most .NET developers simply blocked its helpful warning with "I can't even pronounce
SHGetFolderWhatever, let me just use this key already, I got so far, I need to get this done today.".
Actually, the key was put there by Microsoft developer Raymond Chen, who had the right feeling that people would continue to abuse this key to get the folder path in the future. He wrote some nice articles about why not to use this key here and here.
To cut things a little shorter, using this key was only acceptable in a Windows 95 beta version, and no moment later. Microsoft developers noticed it would not be flexible enough to keep all the information about shell folders, and it did not respect roaming user profiles and so on. A
WinAPI function was created instead to retrieve the real paths, and the key was just left in the registry "temporary" to keep literally four programs designed in the beta time of Win95 compatible with the RTM version. It was never deleted afterwards since more developers didn't read the MSDN documentation, discovered this key in the RTM time, and started to make exorbitant use of it, so deleting it later would make even more programs incompatible than the original four.
So, don't use the values there. They're not guaranteed to be right, or to even exist, and Raymond Chen will probably hate you for doing so - and you better not get into trouble with "Microsoft's Chuck Norris". Even I fell into the "Shell Folders"-key trap once and posted this as a "solution" to a StackOverflow answer (which I have updated meanwhile). Now I just feel ashamed.
The Correct Solution
So, if we are good developers, we just follow what the key has told us: P/Invoke to the said WinAPI functions! Sadly, P/Invoke is a little nasty, and even I struggled to implement the correct solution for a long time - until I finally ran into wrong results with the mentioned registry key and got the errors I deserved.
Luckily for you, you found this CodeProject article, and I posted my "wrapper" around these functions here. It's just a simple wrapper to retrieve the current, redirected and correct path to the folders as well as what the systems default path would be, not using the whole power of the
SHGet/SetKnownFolder functions to change the paths or impersonating another user to get the path of his special folder.
Please note that my wrapper only makes use of
SHGetKnownFolder function, which is a new function introduced in Windows Vista to replace the older
SHGetFolderPath in XP and earlier. Thus said, you need at least Vista or newer, this will not work for XP. You can however, after following this article, easily develop up a wrapper using the older
SHGetFolderPath function, if you still need to support XP or earlier - I don't.
At first, there's the
KnownFolders with functions similar to
<a href="https://msdn.microsoft.com/en-us/library/system.environment.getfolderpath" target="_blank">System.Environment.GetFolderPath()</a>:
public static class KnownFolders
public static string GetPath(KnownFolder knownFolder)
return GetPath(knownFolder, false);
public static string GetPath(KnownFolder knownFolder, bool defaultUser)
return GetPath(knownFolder, KnownFolderFlags.DontVerify, defaultUser);
public static string GetDefaultPath(KnownFolder knownFolder)
return GetDefaultPath(knownFolder, false);
public static string GetDefaultPath(KnownFolder knownFolder, bool defaultUser)
return GetPath(knownFolder, KnownFolderFlags.DefaultPath | KnownFolderFlags.DontVerify,
public static void Initialize(KnownFolder knownFolder)
public static void Initialize(KnownFolder knownFolder, bool defaultUser)
GetPath(knownFolder, KnownFolderFlags.Create | KnownFolderFlags.Init, defaultUser);
GetPath() functions are pretty self-explaining, especially because I added big summaries to them to explain what they do exactly. Additionally, you can create the special folders if they don't exist yet, with the
Of most interest to you now should be the
KnownFolder type, which is just an enumeration of special folders you can retrieve with the functions. I removed the summaries here which would describe what the folder is for, since when operating system version it exists, and what the default path would be, because that are 94 special folders in total!
public enum KnownFolder
When implementing this enumeration and writing the documentation for it, I followed the list of special folders published in MSDN here.
Internally, the enumeration is mapped to array indices, which contain the real
KNOWNFOLDERID with which the WinAPI is queried. I'm afraid to list 94 rows of GUIDs here, so you just have to download the source code to see them all, but they're actually not really exciting for being the GUIDs they are.
Of last interest could be the
private function of the
KnownFolder class which actually does the legwork to eventually call the WinAPI:
private static string GetPath(KnownFolder knownFolder, KnownFolderFlags flags,
int result = SHGetKnownFolderPath(new Guid(_knownFolderGuids[(int)knownFolder]),
(uint)flags, new IntPtr(defaultUser ? -1 : 0), out IntPtr outPath);
if (result >= 0)
string path = Marshal.PtrToStringUni(outPath);
throw new ExternalException("Unable to retrieve the known folder path. It may not "
+ "be available on this system.", result);
private static extern int SHGetKnownFolderPath(
[MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags, IntPtr hToken,
out IntPtr ppszPath);
private enum KnownFolderFlags : uint
SimpleIDList = 0x00000100,
NotParentRelative = 0x00000200,
DefaultPath = 0x00000400,
Init = 0x00000800,
NoAlias = 0x00001000,
DontUnexpand = 0x00002000,
DontVerify = 0x00004000,
Create = 0x00008000,
NoAppcontainerRedirection = 0x00010000,
AliasOnly = 0x80000000
(Disclaimer: I did not put the imported
SHGetKnownFolderPath function into a separate "
NativeMethods"-like class here as recommended by Microsoft, this is just for simplicity.)
As you can see, it gets the GUID associated with the enumeration value as the index in the long GUID string array (which I did not list), the correct flags to get either the default path, create the folder or just get the current path (s. the
KnownFolderFlags enumeration) and returns the path retrieved as a normal .NET
string. Just how you want it!
Using the Code
From your point of view, using the code is straight forward. This small console application prints all the paths it could retrieve - yep, not all paths may be retrieved as they do not exist or are unsupported by your operating system, that's why there's a
private static void Main()
foreach (KnownFolder knownFolder in Enum.GetValues(typeof(KnownFolder)))
Console.Write("Current Path: ");
Console.Write("Default Path: ");
catch (ExternalException ex)
Console.WriteLine("<Exception> " + ex.ErrorCode);
Normally, you would not need the
try catch, as the user folders can be retrieved most of the time, but as you know, most of the time is not all the time. You may just want to rewrite my internal functions to not throw an exception, but that's up to you.
Due to a request in the comments, I created a NuGet package for this functionality. More information about it on its project site.
It offers additional functionality like modifying the paths with the
SHSetKnownFolderPath method, and allows querying the paths of any user (if you have the required rights).
Note that the class design of the NuGet package is slightly different to be more object oriented with all these new goodies. Consult the Usage section on the project site for a guide.
Points of Interest
We learned: P/Invoke is easy - if we do not have to do it and download the source code from CodeProject. Nah, really, it wasn't that hard, was it? And now, we can have fun with any super special folder path as we want it.
As said, you must implement your own wrapper to the older
SHGetFolderPath function to get an XP-and-earlier-compatible wrapper. That function is indeed just a wrapper to
SHGetKnownFolderPath in versions since Windows Vista, and I personally do not care about decade old, unsupported systems anymore, but you may have due to special customers with special folders. But for XP, the
System.Environment.GetFolderPath() function might just be good enough.
If you like my wrapper, feel free to upvote my original answer on StackOverflow!
- 18.12.2018 - Update NuGet project site link.
- 11.06.2018 - Free string memory, update sample, reword some sentences.
- 21.03.2016 - Added note about newly created NuGet package
- 20.02.2015 - First release