117 lines
4.9 KiB
C#
117 lines
4.9 KiB
C#
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
|
||
namespace FluidExplorer.Services.Shell;
|
||
|
||
/// <summary>
|
||
/// 用外壳自己的 API(SHGetKnownFolderPath)解析系统文件夹,
|
||
/// 保证和资源管理器指向同一批真实位置(含 OneDrive 重定向后的"桌面/文档"等)。
|
||
/// </summary>
|
||
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";
|
||
|
||
/// <summary>此电脑 / 回收站等虚拟外壳对象的解析名,交给外壳取图标与打开。</summary>
|
||
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");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 卷信息(用于侧边栏"此电脑"和状态栏):显示名、总容量、可用空间、就绪状态。
|
||
/// </summary>
|
||
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<DriveItem> Enumerate()
|
||
{
|
||
var list = new List<DriveItem>();
|
||
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;
|
||
}
|
||
}
|