Getting Downloads Folder in C#? [duplicate]

The WinAPI method SHGetKnownFolderPath is the only correct way to retrieve paths to special folders - including the personal ones and the Downloads folder.

There are other ways to get similar results which look promising, but end up with just completely wrong paths on specific systems (for example, combining or hard coding parts of the path or abusing an old registry key). The reason behind that is stated in my CodeProject article, which also lists the full solution. It provides a wrapping class with a support of retrieving all known 94 special folders, and some more goodies.

For a quick example here, I just pasted a shortened version of the solution, being able to retrieve only the personal special folders, like Downloads:

using System;
using System.Runtime.InteropServices;

/// <summary>
/// Class containing methods to retrieve specific file system paths.
/// </summary>
public static class KnownFolders
{
    private static string[] _knownFolderGuids = new string[]
    {
        "{56784854-C6CB-462B-8169-88E350ACB882}", // Contacts
        "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}", // Desktop
        "{FDD39AD0-238F-46AF-ADB4-6C85480369C7}", // Documents
        "{374DE290-123F-4565-9164-39C4925E467B}", // Downloads
        "{1777F761-68AD-4D8A-87BD-30B759FA33DD}", // Favorites
        "{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}", // Links
        "{4BD8D571-6D19-48D3-BE97-422220080E43}", // Music
        "{33E28130-4E1E-4676-835A-98395C3BC3BB}", // Pictures
        "{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}", // SavedGames
        "{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}", // SavedSearches
        "{18989B1D-99B5-455B-841C-AB7C74E4DDFC}", // Videos
    };

    /// <summary>
    /// Gets the current path to the specified known folder as currently configured. This does
    /// not require the folder to be existent.
    /// </summary>
    /// <param name="knownFolder">The known folder which current path will be returned.</param>
    /// <returns>The default path of the known folder.</returns>
    /// <exception cref="System.Runtime.InteropServices.ExternalException">Thrown if the path
    ///     could not be retrieved.</exception>
    public static string GetPath(KnownFolder knownFolder)
    {
        return GetPath(knownFolder, false);
    }

    /// <summary>
    /// Gets the current path to the specified known folder as currently configured. This does
    /// not require the folder to be existent.
    /// </summary>
    /// <param name="knownFolder">The known folder which current path will be returned.</param>
    /// <param name="defaultUser">Specifies if the paths of the default user (user profile
    ///     template) will be used. This requires administrative rights.</param>
    /// <returns>The default path of the known folder.</returns>
    /// <exception cref="System.Runtime.InteropServices.ExternalException">Thrown if the path
    ///     could not be retrieved.</exception>
    public static string GetPath(KnownFolder knownFolder, bool defaultUser)
    {
        return GetPath(knownFolder, KnownFolderFlags.DontVerify, defaultUser);
    }

    private static string GetPath(KnownFolder knownFolder, KnownFolderFlags flags,
        bool defaultUser)
    {
        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);
            Marshal.FreeCoTaskMem(outPath);
            return path;
        }
        else
        {
            throw new ExternalException("Unable to retrieve the known folder path. It may not "
                + "be available on this system.", result);
        }
    }

    [DllImport("Shell32.dll")]
    private static extern int SHGetKnownFolderPath(
        [MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags, IntPtr hToken,
        out IntPtr ppszPath);

    [Flags]
    private enum KnownFolderFlags : uint
    {
        SimpleIDList              = 0x00000100,
        NotParentRelative         = 0x00000200,
        DefaultPath               = 0x00000400,
        Init                      = 0x00000800,
        NoAlias                   = 0x00001000,
        DontUnexpand              = 0x00002000,
        DontVerify                = 0x00004000,
        Create                    = 0x00008000,
        NoAppcontainerRedirection = 0x00010000,
        AliasOnly                 = 0x80000000
    }
}

/// <summary>
/// Standard folders registered with the system. These folders are installed with Windows Vista
/// and later operating systems, and a computer will have only folders appropriate to it
/// installed.
/// </summary>
public enum KnownFolder
{
    Contacts,
    Desktop,
    Documents,
    Downloads,
    Favorites,
    Links,
    Music,
    Pictures,
    SavedGames,
    SavedSearches,
    Videos
}

(A fully commented version is found in the CodeProject article linked above.)

While this was just a nasty wall of code, the surface you have to deal with is pretty simple. Here's an example of a console program outputting the path of the Downloads folder.

private static void Main()
{
    string downloadsPath = KnownFolders.GetPath(KnownFolder.Downloads);
    Console.WriteLine("Downloads folder path: " + downloadsPath);
    Console.ReadLine();
}

E.g., just call KnownFolders.GetPath() with the KnownFolder enum value of the folder which path you want to query.

NuGet Package

If you don't want to go through all this hazzle, just install my NuGet package I recently created. Here's the project site, and here's the gallery link (note that the usage is different and polished, please consult the Usage section on the project site for more information).

PM> Install-Package Syroot.Windows.IO.KnownFolders

Usage:

using System;
using Syroot.Windows.IO;

class Program
{
    static void Main(string[] args)
    {
        string downloadsPath = new KnownFolder(KnownFolderType.Downloads).Path;
        Console.WriteLine("Downloads folder path: " + downloadsPath);
        Console.ReadLine();
    }
}

The easiest way is:

Process.Start("shell:Downloads");

If you only need to get the current user's download folder path, you can use this:

I extracted it from @PacMani 's code.

 // using Microsoft.Win32;
string GetDownloadFolderPath() 
{
    return Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", "{374DE290-123F-4565-9164-39C4925E467B}", String.Empty).ToString();
}

http://msdn.microsoft.com/en-us//library/system.environment.specialfolder.aspx

There are the variables with the path to some special folders.

Cross-Platform version:

public static string getHomePath()
{
    // Not in .NET 2.0
    // System.Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
    if (System.Environment.OSVersion.Platform == System.PlatformID.Unix)
        return System.Environment.GetEnvironmentVariable("HOME");

    return System.Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
}


public static string getDownloadFolderPath()
{
    if (System.Environment.OSVersion.Platform == System.PlatformID.Unix)
    {
        string pathDownload = System.IO.Path.Combine(getHomePath(), "Downloads");
        return pathDownload;
    }

    return System.Convert.ToString(
        Microsoft.Win32.Registry.GetValue(
             @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
            ,"{374DE290-123F-4565-9164-39C4925E467B}"
            ,String.Empty
        )
    );
}

typically, your software shall have a configurable variable that stores the user's download folder, which can be assigned by the user, and provide a default value when not set. You can store the value in app config file or the registry.

Then in your code read the value from where it's stored.

string download = Environment.GetEnvironmentVariable("USERPROFILE")[email protected]"\"+"Downloads";