disk-info-helper
selenium-helper (Not fully tested) sync-helper (Not fully tested)
This commit is contained in:
503
Helpers/DiskInfoHelper.cs
Normal file
503
Helpers/DiskInfoHelper.cs
Normal file
@ -0,0 +1,503 @@
|
||||
using File_Watcher.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace File_Watcher.Helpers;
|
||||
|
||||
internal static partial class DiskInfoHelper
|
||||
{
|
||||
|
||||
public record DiskDrive(string? Caption,
|
||||
string? DeviceID,
|
||||
string? FirmwareRevision,
|
||||
uint? Index,
|
||||
string? Model,
|
||||
string? Name,
|
||||
uint? Partitions,
|
||||
DiskPartition[]? PartitionCollection,
|
||||
string? SerialNumber,
|
||||
ulong? Size,
|
||||
string? Status,
|
||||
string? SystemName,
|
||||
ulong? TotalCylinders,
|
||||
ulong? TotalHeads)
|
||||
{
|
||||
|
||||
internal static DiskDrive Get(DiskDrive diskDrive, DiskPartition[] diskPartitions, LogicalDrive[] logicalDrives)
|
||||
{
|
||||
DiskDrive result;
|
||||
DiskPartition[] collection = DiskPartition.Get((from l in diskPartitions where l.DiskIndex == diskDrive.Index select l).ToArray(), logicalDrives);
|
||||
result = new(Caption: diskDrive.Caption,
|
||||
DeviceID: diskDrive.DeviceID,
|
||||
FirmwareRevision: diskDrive.FirmwareRevision,
|
||||
Index: diskDrive.Index,
|
||||
Model: diskDrive.Model,
|
||||
Name: diskDrive.Name,
|
||||
Partitions: diskDrive.Partitions,
|
||||
PartitionCollection: collection,
|
||||
SerialNumber: diskDrive.SerialNumber,
|
||||
Size: diskDrive.Size,
|
||||
Status: diskDrive.Status,
|
||||
SystemName: diskDrive.SystemName,
|
||||
TotalCylinders: diskDrive.TotalCylinders,
|
||||
TotalHeads: diskDrive.TotalHeads);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(DiskDrive[]))]
|
||||
private partial class DiskDriveArraySourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>))]
|
||||
private partial class DictionaryStringObjectSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
private partial class JsonElementSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
public record DiskPartition(bool? BootPartition,
|
||||
string? Caption,
|
||||
string? Description,
|
||||
string? DeviceID,
|
||||
uint? DiskIndex,
|
||||
uint? Index,
|
||||
LogicalDrive[]? LogicalDrives,
|
||||
ulong? Size,
|
||||
ulong? StartingOffset,
|
||||
string? SystemName,
|
||||
string? Type)
|
||||
{
|
||||
|
||||
internal static DiskPartition[] Get(DiskPartition[] diskPartitions, LogicalDrive[] logicalDrives)
|
||||
{
|
||||
List<DiskPartition> results = [];
|
||||
ulong lowerMatch;
|
||||
ulong upperMatch;
|
||||
LogicalDrive[] collection;
|
||||
DiskPartition diskPartition;
|
||||
foreach (DiskPartition p in diskPartitions)
|
||||
{
|
||||
if (p.Size is null)
|
||||
continue;
|
||||
lowerMatch = p.Size.Value - 10240;
|
||||
upperMatch = p.Size.Value + 10240;
|
||||
collection = (from l in logicalDrives where l.DriveType != 4 && l.Size > lowerMatch && l.Size < upperMatch select l).ToArray();
|
||||
diskPartition = new(BootPartition: p.BootPartition,
|
||||
Caption: p.Caption,
|
||||
Description: p.Description,
|
||||
DeviceID: p.DeviceID,
|
||||
DiskIndex: p.DiskIndex,
|
||||
Index: p.Index,
|
||||
LogicalDrives: collection,
|
||||
Size: p.Size,
|
||||
StartingOffset: p.StartingOffset,
|
||||
SystemName: p.SystemName,
|
||||
Type: p.Type);
|
||||
results.Add(diskPartition);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(DiskPartition[]))]
|
||||
private partial class DiskPartitionSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
public record LogicalDrive(string? Caption,
|
||||
string? DeviceID,
|
||||
uint? DriveType,
|
||||
string? FileSystem,
|
||||
ulong? FreeSpace,
|
||||
string? Name,
|
||||
ulong? PercentageFree,
|
||||
ulong? PercentageUsed,
|
||||
string? ProviderName,
|
||||
ulong? Size,
|
||||
string? SystemName,
|
||||
string? VolumeName,
|
||||
string? VolumeSerialNumber);
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(LogicalDrive[]))]
|
||||
private partial class LogicalDriveSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
public record Record(long AvailableFreeSpace,
|
||||
string DriveFormat,
|
||||
string DriveType,
|
||||
bool IsReady,
|
||||
char Name,
|
||||
ulong PercentageFree,
|
||||
ulong PercentageUsed,
|
||||
long TotalFreeSpace,
|
||||
long TotalSize,
|
||||
string VolumeLabel)
|
||||
{
|
||||
|
||||
public static Record Get(DriveInfo driveInfo) =>
|
||||
new(AvailableFreeSpace: driveInfo.AvailableFreeSpace,
|
||||
DriveFormat: driveInfo.DriveFormat,
|
||||
DriveType: driveInfo.DriveType.ToString(),
|
||||
IsReady: driveInfo.IsReady,
|
||||
Name: driveInfo.Name.ToUpper()[0],
|
||||
PercentageUsed: (ulong)(Math.Round(driveInfo.AvailableFreeSpace / (double)driveInfo.TotalSize, 2) * 100),
|
||||
PercentageFree: (ulong)(Math.Round((driveInfo.TotalSize - driveInfo.AvailableFreeSpace) / (double)driveInfo.TotalSize, 2) * 100),
|
||||
TotalFreeSpace: driveInfo.TotalFreeSpace,
|
||||
TotalSize: driveInfo.TotalSize,
|
||||
VolumeLabel: driveInfo.VolumeLabel);
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Record[]))]
|
||||
private partial class RecordSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
private record Win32(string? DiskDrive,
|
||||
string? Processor,
|
||||
string? LogicalDisk,
|
||||
string? DiskPartition,
|
||||
string? NetworkAdapter);
|
||||
|
||||
internal static bool WriteDiskInfo(AppSettings appSettings, ILogger<Worker> logger)
|
||||
{
|
||||
string? json = WriteRecord();
|
||||
ReadOnlyCollection<DiskDrive> devices = GetDrives(appSettings, logger);
|
||||
string yaml = GetYetAnotherMarkupLanguage(devices);
|
||||
ReadOnlyCollection<string> normalized = GetNormalized(appSettings, logger, devices);
|
||||
string text = string.Join(Environment.NewLine, normalized);
|
||||
string markdown = string.Concat("# ",
|
||||
Environment.MachineName.ToLower(),
|
||||
Environment.NewLine,
|
||||
Environment.NewLine,
|
||||
"## Normalized",
|
||||
Environment.NewLine,
|
||||
Environment.NewLine,
|
||||
"```log",
|
||||
Environment.NewLine,
|
||||
text,
|
||||
Environment.NewLine,
|
||||
"```",
|
||||
Environment.NewLine,
|
||||
Environment.NewLine,
|
||||
"## json",
|
||||
Environment.NewLine,
|
||||
Environment.NewLine,
|
||||
"```json",
|
||||
Environment.NewLine,
|
||||
json,
|
||||
Environment.NewLine,
|
||||
"```",
|
||||
Environment.NewLine,
|
||||
Environment.NewLine,
|
||||
"## yaml",
|
||||
Environment.NewLine,
|
||||
Environment.NewLine,
|
||||
"```yaml",
|
||||
Environment.NewLine,
|
||||
yaml,
|
||||
Environment.NewLine,
|
||||
"```",
|
||||
Environment.NewLine);
|
||||
WriteAllText(Path.Combine(appSettings.DiskInfoConfiguration.Destination, $"{Environment.MachineName.ToLower()}.md"), markdown);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string WriteRecord()
|
||||
{
|
||||
string result;
|
||||
Record record;
|
||||
List<Record> records = [];
|
||||
DriveInfo[] drives = DriveInfo.GetDrives();
|
||||
foreach (DriveInfo driveInfo in drives)
|
||||
{
|
||||
record = Record.Get(driveInfo);
|
||||
records.Add(record);
|
||||
}
|
||||
result = JsonSerializer.Serialize(records.ToArray(), RecordSourceGenerationContext.Default.RecordArray);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<DiskDrive> GetDrives(AppSettings appSettings, ILogger<Worker> logger)
|
||||
{
|
||||
#pragma warning disable CA1416
|
||||
List<DiskDrive> results = [];
|
||||
Win32 win32 = GetWin32(appSettings, logger);
|
||||
if (win32.DiskDrive is not null && win32.DiskPartition is not null && win32.LogicalDisk is not null)
|
||||
{
|
||||
DiskDrive diskDrive;
|
||||
string diskDriveJson = win32.DiskDrive[0] == '[' ? win32.DiskDrive : $"[{win32.DiskDrive}]";
|
||||
string logicalDiskJson = win32.LogicalDisk[0] == '[' ? win32.LogicalDisk : $"[{win32.LogicalDisk}]";
|
||||
string diskPartitionJson = win32.DiskPartition[0] == '[' ? win32.DiskPartition : $"[{win32.DiskPartition}]";
|
||||
DiskDrive[] diskDrives = JsonSerializer.Deserialize(diskDriveJson, DiskDriveArraySourceGenerationContext.Default.DiskDriveArray) ??
|
||||
throw new NullReferenceException(nameof(DiskDrive));
|
||||
LogicalDrive[] logicalDrives = JsonSerializer.Deserialize(logicalDiskJson, LogicalDriveSourceGenerationContext.Default.LogicalDriveArray) ??
|
||||
throw new NullReferenceException(nameof(LogicalDrive));
|
||||
DiskPartition[] diskPartitions = JsonSerializer.Deserialize(diskPartitionJson, DiskPartitionSourceGenerationContext.Default.DiskPartitionArray) ??
|
||||
throw new NullReferenceException(nameof(DiskPartition));
|
||||
foreach (DiskDrive d in diskDrives)
|
||||
{
|
||||
diskDrive = DiskDrive.Get(d, diskPartitions, logicalDrives);
|
||||
results.Add(diskDrive);
|
||||
}
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
#pragma warning restore
|
||||
}
|
||||
|
||||
private static Win32 GetWin32(AppSettings appSettings, ILogger<Worker> logger)
|
||||
{
|
||||
Win32 win32;
|
||||
Process? process;
|
||||
string[] segments;
|
||||
string standardError;
|
||||
string standardOutput;
|
||||
string? diskDrive = null;
|
||||
string? processor = null;
|
||||
string? logicalDisk = null;
|
||||
string? diskPartition = null;
|
||||
string? networkAdapter = null;
|
||||
ProcessStartInfo processStartInfo;
|
||||
string fileName = "powershell.exe";
|
||||
string date = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
foreach (string className in appSettings.DiskInfoConfiguration.Classes)
|
||||
{
|
||||
segments = className.Split(' ');
|
||||
if (segments[0].Length < 3)
|
||||
continue;
|
||||
processStartInfo = new(fileName, $"-NoProfile -ExecutionPolicy ByPass Get-WmiObject {className} | ConvertTo-JSON")
|
||||
{
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
try
|
||||
{
|
||||
process = Process.Start(processStartInfo);
|
||||
if (process is not null)
|
||||
{
|
||||
for (int j = 1; j < 45; j++)
|
||||
{
|
||||
_ = process.WaitForExit(1000);
|
||||
if (process.HasExited)
|
||||
break;
|
||||
}
|
||||
if (!process.HasExited)
|
||||
{
|
||||
logger.LogWarning($"// {segments[0]} Never exited!");
|
||||
if (segments[0] is "Win32_DiskDrive" or "Win32_DiskPartition" or "Win32_LogicalDisk")
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
standardError = process.StandardError.ReadToEnd();
|
||||
standardOutput = process.StandardOutput.ReadToEnd();
|
||||
logger.LogInformation("// {className}{line}{error}{line}{line}// {output}", segments[0], Environment.NewLine, standardError, Environment.NewLine, Environment.NewLine, standardOutput);
|
||||
if (string.IsNullOrEmpty(standardError))
|
||||
{
|
||||
if (segments[0] == "Win32_DiskDrive")
|
||||
diskDrive = standardOutput;
|
||||
else if (segments[0] == "Win32_DiskPartition")
|
||||
diskPartition = standardOutput;
|
||||
else if (segments[0] == "Win32_LogicalDisk")
|
||||
logicalDisk = standardOutput;
|
||||
else if (segments[0] == "Win32_NetworkAdapter")
|
||||
networkAdapter = standardOutput;
|
||||
else if (segments[0] == "Win32_Processor")
|
||||
processor = standardOutput;
|
||||
WriteAllText(Path.Combine(appSettings.DiskInfoConfiguration.Destination, $"{Environment.MachineName.ToLower()}-{segments[0]}-{date}.json"), standardOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
process?.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error:");
|
||||
}
|
||||
}
|
||||
win32 = new(DiskDrive: diskDrive,
|
||||
Processor: processor,
|
||||
LogicalDisk: logicalDisk,
|
||||
DiskPartition: diskPartition,
|
||||
NetworkAdapter: networkAdapter);
|
||||
return win32;
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> DeserializeAndFlatten(string json, char? remove)
|
||||
{
|
||||
Dictionary<string, object> results = [];
|
||||
JsonElement jsonElement = JsonSerializer.Deserialize(json, JsonElementSourceGenerationContext.Default.JsonElement);
|
||||
FillDictionaryFromJToken(results, jsonElement, string.Empty, remove);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void FillDictionaryFromJToken(Dictionary<string, object> results, JsonElement jsonElement, string prefix, char? remove)
|
||||
{
|
||||
switch (jsonElement.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
foreach (JsonProperty jsonProperty in jsonElement.EnumerateObject())
|
||||
{
|
||||
FillDictionaryFromJToken(results, jsonProperty.Value, Join(prefix, jsonProperty.Name), remove);
|
||||
}
|
||||
break;
|
||||
|
||||
case JsonValueKind.Array:
|
||||
int index = 0;
|
||||
foreach (JsonElement value in jsonElement.EnumerateArray())
|
||||
{
|
||||
FillDictionaryFromJToken(results, value, Join(prefix, index.ToString()), remove);
|
||||
index++;
|
||||
}
|
||||
break;
|
||||
|
||||
case JsonValueKind.String:
|
||||
if (remove is null)
|
||||
{
|
||||
results.Add(prefix, jsonElement.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add(prefix, jsonElement.ToString().Replace(remove.Value, '_'));
|
||||
}
|
||||
break;
|
||||
|
||||
case JsonValueKind.True:
|
||||
case JsonValueKind.False:
|
||||
case JsonValueKind.Number:
|
||||
results.Add(prefix, jsonElement.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Join(string prefix, string name) =>
|
||||
string.IsNullOrEmpty(prefix) ? name : prefix + "." + name;
|
||||
|
||||
private static string GetYetAnotherMarkupLanguage(ReadOnlyCollection<DiskDrive> devices)
|
||||
{
|
||||
string result;
|
||||
List<string> results = [];
|
||||
string json = JsonSerializer.Serialize(devices.ToArray(), DiskDriveArraySourceGenerationContext.Default.DiskDriveArray);
|
||||
Dictionary<string, object> keyValuePairs = DeserializeAndFlatten(json, ':');
|
||||
string[] lines = JsonSerializer.Serialize(keyValuePairs, DictionaryStringObjectSourceGenerationContext.Default.DictionaryStringObject).Split(Environment.NewLine);
|
||||
for (int i = 1; i < lines.Length - 1; i++)
|
||||
{
|
||||
results.Add(lines[i].Trim()
|
||||
.Trim(',')
|
||||
.Trim('"')
|
||||
.Replace("\": \"", ": ")
|
||||
.Replace("\": ", ": "));
|
||||
}
|
||||
result = string.Join(Environment.NewLine, results);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string GetSizeWithSuffix(ulong value)
|
||||
{
|
||||
string result;
|
||||
int i = 0;
|
||||
double displayValue = value * 1f;
|
||||
string[] SizeSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
while (Math.Round(displayValue / 1024f) >= 1)
|
||||
{
|
||||
displayValue /= 1024;
|
||||
i++;
|
||||
}
|
||||
result = string.Format("{0:n1} {1}", displayValue, SizeSuffixes[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void WriteAllText(string path, string text)
|
||||
{
|
||||
string check = !File.Exists(path) ? string.Empty : File.ReadAllText(path);
|
||||
if (check != text)
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<string> GetNormalized(AppSettings appSettings, ILogger<Worker> logger, ReadOnlyCollection<DiskDrive> drives)
|
||||
{
|
||||
List<string> results = [];
|
||||
decimal free;
|
||||
decimal used;
|
||||
string display;
|
||||
int deviceCount;
|
||||
int offsetCount;
|
||||
string deviceSize;
|
||||
int partitionCount;
|
||||
results.Add("Max");
|
||||
string offsetDisplay;
|
||||
string partitionSize;
|
||||
decimal devicePercent;
|
||||
string logicalDriveUsed;
|
||||
string logicalDriveSize;
|
||||
decimal partitionPercent;
|
||||
int logicalDriveFreeCount;
|
||||
int logicalDriveUsedCount;
|
||||
decimal partitionDecimalSize;
|
||||
string logicalDriveFreeSpace;
|
||||
decimal logicalDriveDecimalFreeSpace;
|
||||
DiskInfoConfiguration diskInfoConfiguration = appSettings.DiskInfoConfiguration;
|
||||
results.Add($"{new string('-', diskInfoConfiguration.Bars)} Value of Max Argument");
|
||||
foreach (DiskDrive drive in drives.OrderBy(l => l.Index))
|
||||
{
|
||||
if (drive.Size is null || drive.PartitionCollection is null)
|
||||
continue;
|
||||
devicePercent = drive.Size.Value / diskInfoConfiguration.Max;
|
||||
deviceSize = GetSizeWithSuffix(drive.Size.Value);
|
||||
results.Add($"{drive.DeviceID} - Cylinders: {drive.TotalCylinders} - {drive.Model}");
|
||||
deviceCount = (int)Math.Round(devicePercent * diskInfoConfiguration.Bars);
|
||||
results.Add($"{new string('-', deviceCount)} {Math.Round(devicePercent, 2) * 100:000}% of Max Argument {deviceSize}");
|
||||
foreach (DiskPartition partition in drive.PartitionCollection.OrderBy(l => l.Index))
|
||||
{
|
||||
if (partition.Size is null || partition.StartingOffset is null || partition.LogicalDrives is null)
|
||||
continue;
|
||||
partitionPercent = partition.Size.Value / diskInfoConfiguration.Max;
|
||||
partitionSize = GetSizeWithSuffix(partition.Size.Value);
|
||||
partitionCount = (int)Math.Round(partitionPercent * diskInfoConfiguration.Bars);
|
||||
offsetCount = (int)Math.Round(partition.StartingOffset.Value / diskInfoConfiguration.Max * diskInfoConfiguration.Bars);
|
||||
offsetDisplay = new string('_', offsetCount);
|
||||
results.Add($"{partition.DeviceID} - {string.Join(", ", partition.LogicalDrives.Select(l => l.DeviceID))}");
|
||||
results.Add($"{offsetDisplay}{new string('-', partitionCount)}{new string('_', diskInfoConfiguration.Bars - offsetCount - partitionCount)} {Math.Round(partitionPercent, 2) * 100:000}% of Disk {partitionSize}");
|
||||
foreach (LogicalDrive logicalDrive in partition.LogicalDrives)
|
||||
{
|
||||
if (logicalDrive.Size is null || logicalDrive.FreeSpace is null)
|
||||
continue;
|
||||
if ((int)Math.Round(logicalDrive.Size.Value / diskInfoConfiguration.Max * diskInfoConfiguration.Bars) != (int)Math.Round(partition.Size.Value / diskInfoConfiguration.Max * diskInfoConfiguration.Bars))
|
||||
{
|
||||
logger.LogWarning("logicalDrive.Size !~ partition.Size");
|
||||
break;
|
||||
}
|
||||
partitionDecimalSize = partition.Size.Value;
|
||||
logicalDriveDecimalFreeSpace = logicalDrive.FreeSpace.Value;
|
||||
logicalDriveSize = GetSizeWithSuffix(logicalDrive.Size.Value);
|
||||
logicalDriveFreeSpace = GetSizeWithSuffix(logicalDrive.FreeSpace.Value);
|
||||
logicalDriveUsed = GetSizeWithSuffix(logicalDrive.Size.Value - logicalDrive.FreeSpace.Value);
|
||||
logicalDriveFreeCount = (int)Math.Round(logicalDriveDecimalFreeSpace / diskInfoConfiguration.Max * diskInfoConfiguration.Bars);
|
||||
display = string.IsNullOrEmpty(logicalDrive.DeviceID) ? "Partition" : logicalDrive.DeviceID;
|
||||
logicalDriveUsedCount = (int)Math.Round((partitionDecimalSize - logicalDriveDecimalFreeSpace) / diskInfoConfiguration.Max * diskInfoConfiguration.Bars);
|
||||
free = (int)(Math.Round(logicalDriveDecimalFreeSpace / partitionDecimalSize, 2) * 100);
|
||||
used = (int)(Math.Round((partitionDecimalSize - logicalDriveDecimalFreeSpace) / partitionDecimalSize, 2) * 100);
|
||||
results.Add($"{offsetDisplay}{new string('-', logicalDriveUsedCount)}{new string('*', logicalDriveFreeCount)}{new string('_', diskInfoConfiguration.Bars - offsetCount - logicalDriveUsedCount - logicalDriveFreeCount)} {used:000}% of [{display}] {logicalDriveUsed} ({free:000}% {logicalDriveFreeSpace} free)");
|
||||
}
|
||||
}
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user