using System.Runtime.InteropServices; using System.Text; namespace FluidExplorer.Services.Shell; /// /// 用外壳自己的 API(SHGetKnownFolderPath)解析系统文件夹, /// 保证和资源管理器指向同一批真实位置(含 OneDrive 重定向后的"桌面/文档"等)。 /// public static class KnownFolders { public static string Profile { get; } = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); public static string Desktop { get; } = Get(FolderId.Desktop); public static string Documents { get; } = Get(FolderId.Documents); public static string Downloads { get; } = Get(FolderId.Downloads); public static string Pictures { get; } = Get(FolderId.Pictures); public static string Music { get; } = Get(FolderId.Music); public static string Videos { get; } = Get(FolderId.Videos); public static string RecycleBin { get; } = @"shell:RecycleBinFolder"; /// 此电脑 / 回收站等虚拟外壳对象的解析名,交给外壳取图标与打开。 public const string ThisPcParsingName = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"; public const string RecycleBinParsingName = "::{645FF040-5081-101B-9F08-00AA002F954E}"; public const string NetworkParsingName = "::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}"; public const string HomeParsingName = "::{F874310E-B6B7-47DC-BC84-B9E6B38F5903}"; // 主页 public const string GalleryParsingName = "::{E88865EA-0E1C-4E20-9AA6-EDCD0212C87C}"; // 图库 private static string Get(Guid id) { try { var hr = SHGetKnownFolderPath(ref id, 0, IntPtr.Zero, out var ptr); if (hr != 0 || ptr == IntPtr.Zero) return string.Empty; try { return Marshal.PtrToStringUni(ptr) ?? string.Empty; } finally { Marshal.FreeCoTaskMem(ptr); } } catch { return string.Empty; } } [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] private static extern int SHGetKnownFolderPath(ref Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr ppszPath); private static class FolderId { public static Guid Desktop = new("B4BFCC3A-DB2C-424C-B029-7FE99A87C641"); public static Guid Documents = new("FDD39AD0-238F-46AF-ADB4-6C85480369C7"); public static Guid Downloads = new("374DE290-123F-4565-9164-39C4925E467B"); public static Guid Pictures = new("33E28130-4E1E-4676-835A-98395C3BC3BB"); public static Guid Music = new("4BD8D571-6D19-48D3-BE97-422220080E43"); public static Guid Videos = new("18989B1D-99B5-455B-841C-AB7C74E4DDFC"); } } /// /// 卷信息(用于侧边栏"此电脑"和状态栏):显示名、总容量、可用空间、就绪状态。 /// public sealed class DriveItem { public required string RootPath { get; init; } public required string DisplayName { get; init; } public required string VolumeLabel { get; init; } public required string FileSystem { get; init; } public long TotalBytes { get; init; } public long FreeBytes { get; init; } public int DriveType { get; init; } public bool IsReady { get; init; } public double UsedRatio => TotalBytes <= 0 ? 0 : 1.0 - (double)FreeBytes / TotalBytes; public string CapacityText => !IsReady || TotalBytes <= 0 ? "不可用" : $"{FluidExplorer.Models.FileEntry.FormatSize(FreeBytes)} 可用,共 {FluidExplorer.Models.FileEntry.FormatSize(TotalBytes)}"; public string Glyph => DriveType switch { 2 => "\uE88E", // 可移动磁盘 3 => "\uEDA2", // 本地磁盘 4 => "\uE8CE", // 网络驱动器 5 => "\uE958", // 光驱 _ => "\uEDA2" }; public static IReadOnlyList Enumerate() { var list = new List(); foreach (var drive in DriveInfo.GetDrives()) { try { var label = drive.IsReady ? drive.VolumeLabel : string.Empty; var display = string.IsNullOrWhiteSpace(label) ? (drive.Name.TrimEnd('\\') is { Length: > 0 } letter ? $"本地磁盘 ({letter})" : drive.Name) : $"{label} ({drive.Name.TrimEnd('\\')})"; list.Add(new DriveItem { RootPath = drive.Name, DisplayName = display, VolumeLabel = label, FileSystem = drive.IsReady ? drive.DriveFormat : string.Empty, TotalBytes = drive.IsReady ? drive.TotalSize : 0, FreeBytes = drive.IsReady ? drive.TotalFreeSpace : 0, DriveType = (int)drive.DriveType, IsReady = drive.IsReady }); } catch { // 未就绪的驱动器(空读卡器等)直接跳过 } } return list; } }