Как программно получить папку «Загрузки Windows» %USERPROFILE%/Downloads?

В .NET мы можем получить пути к «специальным папкам», таким как «Документы/Рабочий стол» и т. д. Сегодня я попытался найти способ получить путь к папке «Загрузки», но, похоже, он недостаточно особенный.

Я знаю, что могу просто сделать «C:\Users\Username\Downloads», но это кажется уродливым решением. Итак, как я могу получить путь с помощью .NET?


person Muis    schedule 25.09.2010    source источник


Ответы (9)


Проблема вашего первого ответа в том, что он даст вам НЕПРАВИЛЬНЫЙ результат, если каталог загрузок по умолчанию будет изменен на [Download1]! Правильный способ сделать это, охватывающий все возможности,

using System;
using System.Runtime.InteropServices;

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

    [Flags] public enum KnownFolderFlags : uint { SimpleIDList = 0x00000100
        , NotParentRelative = 0x00000200, DefaultPath = 0x00000400, Init = 0x00000800
        , NoAlias = 0x00001000, DontUnexpand = 0x00002000, DontVerify = 0x00004000
        , Create = 0x00008000,NoAppcontainerRedirection = 0x00010000, AliasOnly = 0x80000000
    }
    public static string GetPath(string RegStrName, KnownFolderFlags flags, bool defaultUser) {
        IntPtr outPath;
        int result = 
            SHGetKnownFolderPath (
                new Guid(RegStrName), (uint)flags, new IntPtr(defaultUser ? -1 : 0), out outPath
            );
        if (result >= 0)            {
            return Marshal.PtrToStringUni(outPath);
        } else {
            throw new ExternalException("Unable to retrieve the known folder path. It may not "
                + "be available on this system.", result);
        }
    }

}   

Чтобы проверить это, если вы хотите получить свой личный каталог загрузки, установите флажок по умолчанию false --›

using System.IO;

class Program    {
    [STAThread]
    static void Main(string[] args)        {
        string path2Downloads = string.Empty;
        path2Downloads = 
            cGetEnvVars_WinExp.GetPath("{374DE290-123F-4565-9164-39C4925E467B}", cGetEnvVars_WinExp.KnownFolderFlags.DontVerify, false);
        string[] files = { "" };
        if (Directory.Exists(path2Downloads)) {
            files = Directory.GetFiles(path2Downloads);
        }
    }
person Jenna Leaf    schedule 26.10.2017
comment
Ваш первый пример с использованием SHGetKnownFolderPath приведет к утечке памяти. В документации для SHGetKnownFolderPath говорится, что вызывающая сторона должна освободить значение `` ppszPath` с помощью CoTaskMemFree. , а в документации для Marshal.PtrToStringUni сказано, что она не освобождает переданную ему строку. - person Dai; 18.05.2018
comment
Это пытается прочитать защищенную память и мгновенно переводит мое приложение в режим останова на сервере. Я отладил его, и это происходит с переменной result в функции GetPath. Есть ли решение? - person BlackMarker; 13.08.2018
comment
Я проверил историю вашего сообщения, и вы добавили неверный ответ для расширения %USERPROFILE%\Downloads, что дает неверные результаты, если каталог загрузок не находится в %USERPROFILE% (проблема, которую многие ответы наблюдают на SE), поэтому я откатился к предыдущему пересмотр поста, так как первоначальный ответ правильный. Пожалуйста, дайте мне знать, если у вас есть вопросы по этому поводу. - person Ray; 08.11.2020
comment
Обратите внимание, что в этом ответе по-прежнему происходит утечка памяти из-за того, что Marshal.FreeCoTaskMem не вызывается outPath. Для правильного решения, с. Ответ Ганса Пассана. - person Ray; 08.11.2020

Да, это особенное, узнать имя этой папки было невозможно до Vista. .NET по-прежнему должен поддерживать предыдущие операционные системы. Вы можете вызвать SHGetKnownFolderPath(), чтобы обойти это ограничение, например:

using System.Runtime.InteropServices;
...

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);
    }
}

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);
person Hans Passant    schedule 25.09.2010
comment
Зачем использовать SHGetKnownFolderPath, а не Environment.SpecialFolder? - person Kiquenet; 14.03.2017
comment
Использование Impersonator с LOGON32_LOGON_INTERACTIVE не работает Environment.GetFolderPath(Environment.SpecialFolder.Personal) и SHGetKnownFolderPath ? - person Kiquenet; 14.03.2017
comment
@Kiquenet: Environment.SpecialFolder не охватывает все известные папки (см. docs.microsoft.com/en-us/dotnet/api/), а папка Загрузки — заметное упущение. - person mklement0; 16.09.2019

Попробуй это:

string path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)+ @"\Downloads";
person Shaahin    schedule 14.04.2019
comment
Неверно. Каталог загрузок (и любой другой пользовательский каталог) может не находиться внутри UserProfile. - person Ray; 08.11.2020
comment
Но это стандарт, я не перемещал и не манипулировал структурой Windows, и там, где она стандартна и работает, и некоторые люди пробовали и голосовали, я думаю, нет причин жаловаться на это. Если у вас есть папка для загрузки в других местах, возможно, у вас другой случай. - person Shaahin; 12.11.2020
comment
Нет. Это встроенная функция Windows, позволяющая изменять расположение пользовательских папок. Просто щелкните папки правой кнопкой мыши, перейдите на вкладку «Расположения» и введите новый путь. И тогда результаты вашего кода совершенно неверны. Код, который просто работает большую часть времени, — это не решение, а способ создания ошибок. Кроме того, здесь и на подобные вопросы есть множество действительно правильных ответов (вы должны использовать SHGetKnownFolderPath для этого), а также другие неправильные ответы, точно такие же, как у вас, поэтому я не знаю, почему вы это опубликовали для начала. . - person Ray; 12.11.2020

Это рефакторинг принятого ответа, так как IMO его можно было бы реализовать немного лучше:

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

    static Dictionary<string, Guid> Map { get; } = new Dictionary<string, Guid> {
        { nameof(Contacts), Contacts },
        { nameof(Desktop), Desktop },
        { nameof(Documents), Documents },
        { nameof(Downloads), Downloads },
        { nameof(Favorites), Favorites },
        { nameof(Links), Links },
        { nameof(Music), Music },
        { nameof(Pictures), Pictures },
        { nameof(SavedGames), SavedGames },
        { nameof(SavedSearches), SavedSearches },
        { nameof(Videos), Videos },
    };

    public static string GetPath(string knownFolder,
        KnownFolderFlags flags = KnownFolderFlags.DontVerify, bool defaultUser = false) =>
        Map.TryGetValue(knownFolder, out var knownFolderId)
            ? GetPath(knownFolderId, flags, defaultUser)
            : ThrowUnknownFolder();

    public static string GetPath(Guid knownFolderId, 
        KnownFolderFlags flags=KnownFolderFlags.DontVerify, bool defaultUser=false)
    {
        if (SHGetKnownFolderPath(knownFolderId, (uint)flags, new IntPtr(defaultUser ? -1 : 0), out var outPath) >= 0)
        {
            string path = Marshal.PtrToStringUni(outPath);
            Marshal.FreeCoTaskMem(outPath);
            return path;
        }
        return ThrowUnknownFolder();
    }

    //[DoesNotReturn]
    static string ThrowUnknownFolder() => 
        throw new NotSupportedException("Unable to retrieve the path for known folder. It may not be available on this system.");

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

Можно вызвать с помощью:

var downloadPath = KnownFolders.GetPath(KnownFolders.Downloads);

Или иногда удобнее получить его с помощью строки:

var downloadPath = KnownFolders.GetPath(nameof(KnownFolders.Downloads));
person mythz    schedule 11.05.2020
comment
Если бы я мог что-то предложить, используйте такой словарь: private static Dictionary‹KnownFolder, string› _knownFolderGuids. Я думаю, что вы можете удалить многое, используя такой словарь. - person Vonkel.; 05.01.2021

Ответ Ганса работает отлично! И я понимаю, что это очень старый вопрос, но, поскольку .Net (по какой-то причине) до сих пор не закрыл эту функциональную дыру, я решил опубликовать приведенный ниже рефакторинг ответа Хана на случай, если кто-то найдет его полезным.

  • добавлена ​​недостающая обработка ошибок
  • доступ к папке через свойство вместо метода, т.е. в соответствии с другим использованием среды .Net (например, Environment.CurrentDirectory)

скопировать/вставить фрагмент..

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Utils
{
    public static class SpecialFolder
    {
        public static string Downloads => _downloads ??= GetDownloads();

        // workaround for missing .net feature SpecialFolder.Downloads
        // - https://stackoverflow.com/a/3795159/227110
        // - https://stackoverflow.com/questions/10667012/getting-downloads-folder-in-c
        private static string GetDownloads()
        {
            if (Environment.OSVersion.Version.Major < 6)
                throw new NotSupportedException();

            var pathPtr = IntPtr.Zero;
            try
            {
                if (SHGetKnownFolderPath(ref _folderDownloads, 0, IntPtr.Zero, out pathPtr) != 0)
                    throw new DirectoryNotFoundException();
                return Marshal.PtrToStringUni(pathPtr);
            }
            finally
            {
                Marshal.FreeCoTaskMem(pathPtr);
            }
        }

        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        private static extern int SHGetKnownFolderPath(ref Guid id, int flags, IntPtr token, out IntPtr path);

        private static Guid _folderDownloads = new Guid("374DE290-123F-4565-9164-39C4925E467B");
        private static string _downloads;
    }
}

использование

var downloadFolder = SpecialFolder.Downloads;
person stoj    schedule 12.06.2021

Я использовал приведенный ниже код, и он работает для .net 4.6 с Windows 7 и выше. В приведенном ниже коде указан путь к папке профиля пользователя -> "C:\Users\<username>"

string userProfileFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

Далее, чтобы получить доступ к папке загрузок, просто объедините дополнительные строки пути, как показано ниже:

string DownloadsFolder = userProfileFolder + "\\Downloads\\";

Теперь окончательный результат будет

"C:\Users\<username>\Downloads\"

Надеюсь, это сэкономит время для кого-то в будущем.

person Jabez    schedule 27.07.2018
comment
Как указано в комментариях к другим ответам, делающим то же самое, это дает неправильные результаты, если папка была перемещена из папки профиля пользователя. - person Ray; 25.11.2018

Это не так сложно. Если вы хотите получить каталог папки загрузок в vb.net, просто выполните следующие действия:

1.Добавьте метку с именем Special_Direcories.

2. Добавьте этот код при загрузке формы:

Special_directories.Text = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\downloads\"

Это установит текст метки в качестве пути к пользовательской папке и "\downloads\". Вот как это будет выглядеть:

C:\users\USERFOLDER\downloads\

person VB.NET SOLUTIONS    schedule 16.04.2020
comment
Неверно. Каталог загрузок (и любой другой пользовательский каталог) может не находиться внутри UserProfile. - person Ray; 08.11.2020

пытаться:

Dim Dd As String = Environment.GetFolderPath(Environment.SpecialFolder.Favorites)
Dim downloD As String = Dd.Replace("Favorites", "Downloads")
txt1.text = downLoD

это просто уловка, а не решение.

person Pratama Aditya    schedule 02.10.2016
comment
Публикация ответа на вопрос, опубликованный и на который был дан ответ 6 лет назад. Пожалуйста, объясните, почему ваше решение лучше использовать, чем существующее. - person Gilad Green; 02.10.2016
comment
Объясните, почему ваше решение лучше существующего? Лучше не использовать SHGetKnownFolderPath ? - person Kiquenet; 14.03.2017
comment
Не лучшее решение: The user might have moved the folder to another location (which is quite easy in 8, 8.1 and 10). Получение всех специальных папок в .NET - person Kiquenet; 16.03.2017
comment
Извините, я должен смеяться! Это милый трюк, но он до сих пор называется трюком (не решение — сломается на днях!): заменить избранное на загрузки, ха-ха! Если бы жизнь была такой легкой! (что, если я изменю место загрузки!) - person Jenna Leaf; 26.10.2017

Для ВБ попробуйте...

Dim strNewPath As String = IO.Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)) + "\Downloads\"
person H--    schedule 24.06.2015
comment
Это неправильно, с. мои замечания здесь: codeproject.com/Articles/878605/ - person Ray; 11.07.2015
comment
Не указывайте здесь пути жесткого кода. Пользователь мог переместить папку в другое место (что довольно просто в 8, 8.1 и 10)... - person Simon Mattes; 29.03.2016