【问题标题】:How to programmatically derive Windows Downloads folder "%USERPROFILE%/Downloads"?如何以编程方式派生 Windows 下载文件夹“%USERPROFILE%/Downloads”?
【发布时间】:2011-04-17 05:43:45
【问题描述】:

在 .NET 中,我们可以检索“特殊文件夹”的路径,例如 Documents / Desktop 等。今天我试图找到一种方法来获取“下载”文件夹的路径,但它似乎不够特别。

我知道我只能执行“C:\Users\Username\Downloads”,但这似乎是一个丑陋的解决方案。那么如何使用 .NET 检索路径?

【问题讨论】:

    标签: .net environment-variables windows-vista special-folders


    【解决方案1】:

    是的,它很特别,直到 Vista 才可能发现这个文件夹的名称。 .NET 仍然需要支持以前的操作系统。您可以 pinvoke 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);
    

    【讨论】:

    • 为什么使用SHGetKnownFolderPath而不是Environment.SpecialFolder
    • 使用 Impersonator with LOGON32_LOGON_INTERACTIVE 不起作用Environment.GetFolderPath(Environment.SpecialFolder.Personal) 也不是SHGetKnownFolderPath ?
    • @Kiquenet:Environment.SpecialFolder 没有涵盖所有个已知文件夹(参见docs.microsoft.com/en-us/dotnet/api/…),Downloads文件夹是一个明显的遗漏.
    【解决方案2】:

    对于 VB,试试...

    Dim strNewPath As String = IO.Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)) + "\Downloads\"
    

    【讨论】:

    • 这是错误的,s。我在这里的评论:codeproject.com/Articles/878605/…
    • 不要在这里硬编码路径。用户可能已将文件夹移动到另一个位置(这在 8、8.1 和 10 中非常容易)...
    【解决方案3】:

    尝试:

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

    这只是一个技巧,而不是解决方案。

    【讨论】:

    • 发布 6 年前发布和回答的问题的答案 - 请解释为什么您的解决方案比现有解决方案更好用
    • 请解释为什么您的解决方案比现有解决方案更好用?最好不要使用SHGetKnownFolderPath ?
    • 不是神解决方案: The user might have moved the folder to another location (which is quite easy in 8, 8.1 and 10). Getting All "Special Folders" in .NET
    • 对不起,我不得不笑!这是一个可爱的技巧,但它仍然被称为技巧(不是解决方案 - 它会打破这些日子!):将“收藏夹”替换为“下载”,哈哈!愿生活如此轻松! (如果我改变我的下载位置怎么办!)
    【解决方案4】:

    您的第一个答案的问题是,如果将默认下载目录更改为 [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);
            }
        }
    

    【讨论】:

    • 您使用SHGetKnownFolderPath 的第一个示例将泄漏内存。 SHGetKnownFolderPath 的文档说调用者必须使用 CoTaskMemFree. 释放 ``ppszPath` 值,而 Marshal.PtrToStringUni 的文档说它不会释放传递给它的字符串。
    • 这会尝试读取受保护的内存并立即将我的应用程序置于服务器上的中断模式。我已经对其进行了调试,这发生在GetPath 函数中的result 变量上,有什么解决方案吗?
    • 我检查了你的帖子的历史记录,你添加了一个不正确的答案来展开 %USERPROFILE%\Downloads,如果下载目录不在 %USERPROFILE% 下,则会产生错误的结果(一个问题很多答案在 SE 监督),所以我回滚到帖子的先前版本,因为最初的答案是正确的。如果您对此有任何疑问,请告诉我。
    • 请注意,由于未在outPath 上调用Marshal.FreeCoTaskMem,此答案仍会泄漏内存。对于适当的解决方案,s。汉斯·帕桑特的回答。
    【解决方案5】:

    我使用了下面的代码,它适用于 .net 4.6 和 Windows 7 及更高版本。 下面的代码给出了用户配置文件文件夹路径 -> "C:\Users\&lt;username&gt;"

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

    接下来要访问下载文件夹,只需将附加路径字符串组合如下:

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

    现在,最终结果将是

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

    希望它可以为将来的人节省时间。

    【讨论】:

    • 如 cmets 中对其他答案所做相同操作的说明,如果文件夹已移出用户配置文件文件夹,则会产生错误结果。
    【解决方案6】:

    试试这个:

    string path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)+ @"\Downloads";
    

    【讨论】:

    • 不正确,下载目录(和任何其他用户目录)可能不在 UserProfile 中。
    • 但这是标准的,我没有移动或操纵Windows的结构,它的标准和工作原理,有些人尝试和投票,我认为没有理由抱怨它。如果您在其他地方有下载文件夹,那么您的情况可能有所不同。
    • 没有。更改用户文件夹位置是 Windows 内置功能。只需右键单击文件夹,转到“位置”选项卡,然后输入新路径。然后你的代码的结果是完全错误的。只在“大部分时间”工作的代码不是解决方案,而是一种创建错误的方式。此外,这里和类似问题有许多实际正确的答案(您必须为此使用SHGetKnownFolderPath),以及与您的完全一样的其他错误答案,所以我不知道您为什么将这个发布到开始。
    【解决方案7】:

    这并不难。如果你想在 vb.net 中获取下载文件夹目录,请按照以下步骤操作:

    1.添加一个名为 Special_Direcories 的标签。

    2.在表单加载时添加此代码:

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

    这会将标签文本设置为用户文件夹路径和“\downloads\”。 这就是它的样子:

    C:\users\USERFOLDER\downloads\

    【讨论】:

    • 不正确,下载目录(和任何其他用户目录)可能不在 UserProfile 中。
    【解决方案8】:

    这是对 accepted answer 的重构,因为 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));
    

    【讨论】:

    • 如果我可以提出一些建议,请使用这样的字典:private static Dictionary _knownFolderGuids。我认为你可以通过使用这样的字典来删除很多东西。
    【解决方案9】:

    汉斯的回答完美!我很欣赏这是一个非常古老的问题,但是看到.Net(无论出于何种原因)仍然没有填补这个功能漏洞,我想我会发布下面对 Han 的答案的重构,以防有人发现它有用。

    • 添加了一些缺失的错误处理
    • 通过属性而不是方法访问​​文件夹,即与其他 .Net 环境用法一致(例如 Environment.CurrentDirectory)

    复制/粘贴 sn-p..

    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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-10
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多