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