using System; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; #nullable enable namespace Duplicati.Library.Utility; /// /// Wrapper for SHGetKnownFolderPath to various system folders on Windows /// /// List of GUIDs: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid [SupportedOSPlatform("windows")] public static class SHGetFolder { /// /// Gets the download folder path /// public static string? DownloadFolder => GetKnownFolderPath(new Guid("374DE290-123F-4565-9164-39C4925E467B")); /// /// Gets the user profiles folder path /// public static string? UserProfilesFolder { get { try { // Prefered way, works on Windows Vista and later var shPath = GetKnownFolderPath(new Guid("0762D272-C50A-4BB0-A382-697DCD729B80")); if (!string.IsNullOrWhiteSpace(shPath) && Path.IsPathRooted(shPath) && Directory.Exists(shPath)) return shPath; } catch { } try { // Fallback to "{systemDrive}\\Users" var envPath = Environment.ExpandEnvironmentVariables("%SystemDrive%\\Users"); if (!string.IsNullOrWhiteSpace(envPath) && Path.IsPathRooted(envPath) && Directory.Exists(envPath)) return envPath; } catch { } try { // Fallback to "{systemDrive}\\Users", method 2 var sysPath = Environment.GetFolderPath(Environment.SpecialFolder.System); if (!string.IsNullOrWhiteSpace(sysPath) && Path.IsPathRooted(sysPath) && Directory.Exists(sysPath)) { var sysDir = Path.GetPathRoot(sysPath); if (!string.IsNullOrWhiteSpace(sysDir)) return Path.Combine(sysDir, "Users"); } } catch { } // Most likely, this is the correct path var path = "C:\\Users"; if (Directory.Exists(path)) return path; return null; } } /// /// SHGetKnownFolderPath function to get the folder path /// /// The folder GUID /// Get folder flags /// The access token /// The folder path /// The HRESULT error code [DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int SHGetKnownFolderPath( [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr ppszPath); /// /// Gets the folder path using SHGetKnownFolderPath /// /// The folder GUID /// The folder path private static string? GetKnownFolderPath(Guid folderGuid) { var result = SHGetKnownFolderPath(folderGuid, 0, IntPtr.Zero, out var outPath); if (result != 0) Marshal.ThrowExceptionForHR(result); // Throws an exception for the HRESULT error code var path = Marshal.PtrToStringUni(outPath); Marshal.FreeCoTaskMem(outPath); // Free the memory allocated by SHGetKnownFolderPath return path; } }