Learn how to properly obtain the downloads directory path with C# in WinForms.
In order to get the Downloads directory path in C# properly, some developers could just use a fixed path in a string like "C:\Users\username\Downloads"
especially when the application is private. However, if you are planning on launching your application publicly, there’s a good chance that some users may use a different directory for the Downloads path.
Surprisingly, the downloads directory path cannot be obtained through the EnvironmentFolders.GetPath
method and Environment.SpecialFolders
enum. So what should we do now? We will use the SHGetKnownFolderPath
function available in the Legacy Windows Environment Features (shlobj_core.h header). This method allows you to retrieve the full path of a known folder identified by the folder’s KNOWNFOLDERID. The KNOWNFOLDERID constant represents a GUID that identifies a standard folder registered with the system as Known Folders. These folders are installed with Windows Vista and later operating systems, and a computer will have only folders appropriate to it installed.
In our case, the Downloads folder path is the one we are trying to obtain and the KNOWNFOLDERID of the Downloads folder is 374DE290-123F-4565-9164-39C4925E467B
. Having that, we can proceed with the implementation to obtain the Downloads path.
The following code snippet should do the trick:
// 1. Import InteropServices
using System.Runtime.InteropServices;
/// 2. Declare DownloadsFolder KNOWNFOLDERID
private static Guid FolderDownloads = new Guid("374DE290-123F-4565-9164-39C4925E467B");
/// 3. Import SHGetKnownFolderPath method
/// <summary>
/// Retrieves the full path of a known folder identified by the folder's KnownFolderID.
/// </summary>
/// <param name="id">A KnownFolderID that identifies the folder.</param>
/// <param name="flags">Flags that specify special retrieval options. This value can be
/// 0; otherwise, one or more of the KnownFolderFlag values.</param>
/// <param name="token">An access token that represents a particular user. If this
/// parameter is NULL, which is the most common usage, the function requests the known
/// folder for the current user. Assigning a value of -1 indicates the Default User.
/// The default user profile is duplicated when any new user account is created.
/// Note that access to the Default User folders requires administrator privileges.
/// </param>
/// <param name="path">When this method returns, contains the address of a string that
/// specifies the path of the known folder. The returned path does not include a
/// trailing backslash.</param>
/// <returns>Returns S_OK if successful, or an error value otherwise.</returns>
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern int SHGetKnownFolderPath(ref Guid id, int flags, IntPtr token, out IntPtr path);
/// 4. Declare method that returns the Downloads Path as string
/// <summary>
/// Returns the absolute downloads directory specified on the system.
/// </summary>
/// <returns></returns>
public static string GetDownloadsPath()
{
if (Environment.OSVersion.Version.Major < 6) throw new NotSupportedException();
IntPtr pathPtr = IntPtr.Zero;
try
{
SHGetKnownFolderPath(ref FolderDownloads, 0, IntPtr.Zero, out pathPtr);
return Marshal.PtrToStringUni(pathPtr);
}
finally
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
/// 5. Display the downloads directory in the console
// prints something like: C:\Users\username\Downloads
Console.WriteLine(GetDownloadsPath());
Form example
The following full example should help you to understand the context of where to place every line of code in your own project:
using System;
using System.Windows.Forms;
// 1. Import InteropServices
using System.Runtime.InteropServices;
namespace Sandbox
{
public partial class Form1 : Form
{
/// 2. Declare DownloadsFolder GUI and import SHGetKnownFolderPath method
private static Guid FolderDownloads = new Guid("374DE290-123F-4565-9164-39C4925E467B");
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern int SHGetKnownFolderPath(ref Guid id, int flags, IntPtr token, out IntPtr path);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// 3. When the form loads, display the Downloads directory through a message box
// prints something like: C:\Users\username\Downloads
MessageBox.Show(GetDownloadsPath());
}
/// <summary>
/// Returns the absolute downloads directory specified on the system.
/// </summary>
/// <returns></returns>
public static string GetDownloadsPath()
{
if (Environment.OSVersion.Version.Major < 6) throw new NotSupportedException();
IntPtr pathPtr = IntPtr.Zero;
try
{
SHGetKnownFolderPath(ref FolderDownloads, 0, IntPtr.Zero, out pathPtr);
return Marshal.PtrToStringUni(pathPtr);
}
finally
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
}
}
Happy coding ❤️!