From 1170b94d94174b594a139d70c157c60cecd9c7b2 Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Fri, 4 Oct 2024 00:39:54 +0200 Subject: [PATCH 1/8] initial proof-of-concept fetching disk from VDS --- client/data/Methods/Hardware.cs | 11 +- client/data/Methods/VDS/Enums.cs | 256 ++++++++++++++++++++++++++ client/data/Methods/VDS/Interfaces.cs | 102 ++++++++++ client/data/Methods/VDS/Structs.cs | 104 +++++++++++ client/data/Methods/VDS/VdsClass.cs | 253 +++++++++++++++++++++++++ client/data/Structs.cs | 4 + client/specify_client.csproj | 4 + 7 files changed, 729 insertions(+), 5 deletions(-) create mode 100644 client/data/Methods/VDS/Enums.cs create mode 100644 client/data/Methods/VDS/Interfaces.cs create mode 100644 client/data/Methods/VDS/Structs.cs create mode 100644 client/data/Methods/VDS/VdsClass.cs diff --git a/client/data/Methods/Hardware.cs b/client/data/Methods/Hardware.cs index 2f7d184..5cb5852 100644 --- a/client/data/Methods/Hardware.cs +++ b/client/data/Methods/Hardware.cs @@ -935,11 +935,12 @@ private static List GetPartitionSchemes(List drives) private static async Task GetDiskDriveData() { // "Basic" in this context refers to data we can retrieve directly from WMI without much processing. Model names, partition labels, etc. - List drives = GetBasicDriveInfo(); - drives = GetBasicPartitionInfo(drives); - drives = LinkLogicalPartitions(drives); - drives = LinkNonLogicalPartitions(drives); - drives = GetPartitionSchemes(drives); + //List drives = GetBasicDriveInfo(); + //drives = GetBasicPartitionInfo(drives); + //drives = LinkLogicalPartitions(drives); + //drives = LinkNonLogicalPartitions(drives); + //drives = GetPartitionSchemes(drives); + List drives = Data.Methods.VDS.VDSClass.GetDisksInfo(); drives = GetBitlockerStatus(drives); try diff --git a/client/data/Methods/VDS/Enums.cs b/client/data/Methods/VDS/Enums.cs new file mode 100644 index 0000000..7874d10 --- /dev/null +++ b/client/data/Methods/VDS/Enums.cs @@ -0,0 +1,256 @@ +using System; +using System.Runtime.InteropServices; + +namespace specify_client.Data.Methods.VDS +{ + public enum VDS_DISK_EXTENT_TYPE : int + { + UNKNOWN = 0x00000000, + FREE = 0x00000001, + DATA = 0x00000002, + OEM = 0x00000003, + ESP = 0x00000004, + MSR = 0x00000005, + LDM = 0x00000006, + UNUSABLE = 0x00007FFF + } + + public enum VDS_VOLUME_TYPE : uint + { + UNKNOWN = 0x00000000, + SIMPLE = 0x0000000A, + SPAN = 0x0000000B, + STRIPE = 0x0000000C, + MIRROR = 0x0000000D, + PARITY = 0x0000000E + } + + public enum VDS_VOLUME_STATUS : uint + { + UNKNOWN = 0x00000000, + ONLINE = 0x00000001, + NO_MEDIA = 0x00000003, + OFFLINE = 0x00000004, + FAILED = 0x00000005 + } + + [Flags] + public enum VDS_VOLUME_FLAG : uint + { + SYSTEM_VOLUME = 0x00000001, + BOOT_VOLUME = 0x00000002, + ACTIVE = 0x00000004, + READONLY = 0x00000008, + HIDDEN = 0x00000010, + CAN_EXTEND = 0x00000020, + CAN_SHRINK = 0x00000040, + PAGEFILE = 0x00000080, + HIBERNATION = 0x00000100, + CRASHDUMP = 0x00000200, + INSTALLABLE = 0x00000400, + LBN_REMAP_ENABLED = 0x00000800, + FORMATTING = 0x00001000, + NOT_FORMATTABLE = 0x00002000, + NTFS_NOT_SUPPORTED = 0x00004000, + FAT32_NOT_SUPPORTED = 0x00008000, + FAT_NOT_SUPPORTED = 0x00010000, + NO_DEFAULT_DRIVE_LETTER = 0x00020000, + PERMANENTLY_DISMOUNTED = 0x00040000, + PERMANENT_DISMOUNT_SUPPORTED = 0x00080000, + SHADOW_COPY = 0x00100000, + FVE_ENABLED = 0x00200000, + DIRTY = 0x00400000, + REFS_NOT_SUPPORTED = 0x00800000 + } + + public enum VDS_TRANSITION_STATE : uint + { + UNKNOWN = 0x00000000, + STABLE = 0x00000001, + EXTENDING = 0x00000002, + SHRINKING = 0x00000003, + RECONFIGING = 0x00000004 + } + + public enum VDS_FILE_SYSTEM_TYPE + { + UNKNOWN = 0x00000000, + RAW = 0x00000001, + FAT = 0x00000002, + FAT32 = 0x00000003, + NTFS = 0x00000004, + CDFS = 0x00000005, + UDF = 0x00000006, + EXFAT = 0x00000007, + CSVFS = 0x00000008, + REFS = 0x00000009 + } + + [Flags] + public enum VDS_PROVIDER_FLAG : uint + { + DYNAMIC = 0x1, + INTERNAL_HARDWARE_PROVIDER = 0x2, + ONE_DISK_ONLY_PER_PACK = 0x4, + ONE_PACK_ONLINE_ONLY = 0x8, + VOLUME_SPACE_MUST_BE_CONTIGUOUS = 0x10, + SUPPORT_DYNAMIC = 0x80000000, + SUPPORT_FAULT_TOLERANT = 0x40000000, + SUPPORT_DYNAMIC_1394 = 0x20000000, + SUPPORT_MIRROR = 0x20, + SUPPORT_RAID5 = 0x40 + } + + public enum VDS_PROVIDER_TYPE : uint + { + UNKNOWN = 0, + SOFTWARE = 1, + HARDWARE = 2, + VIRTUALDISK = 3, + MAX = 4 + } + + public enum VDS_PARTITION_STYLE + { + UNKNOWN = 0x00000000, + MBR = 0x00000001, + GPT = 0x00000002 + } + + public enum VDS_STORAGE_BUS_TYPE : uint + { + Unknown = 0, + Scsi = 0x1, + Atapi = 0x2, + Ata = 0x3, + IEEE1394 = 0x4, + Ssa = 0x5, + Fibre = 0x6, + Usb = 0x7, + RAID = 0x8, + iScsi = 0x9, + Sas = 0xa, + Sata = 0xb, + Sd = 0xc, + Mmc = 0xd, + Max = 0xe, + Virtual = 0xe, + FileBackedVirtual = 0xf, + Spaces = 0x10, + NVMe = 0x11, + Scm = 0x12, + Ufs = 0x13, + MaxReserved = 0x7f + } + + [Flags] + public enum VDS_DISK_FLAG + { + AUDIO_CD = 0x1, + HOTSPARE = 0x2, + RESERVE_CAPABLE = 0x4, + MASKED = 0x8, + STYLE_CONVERTIBLE = 0x10, + CLUSTERED = 0x20, + READ_ONLY = 0x40, + SYSTEM_DISK = 0x80, + BOOT_DISK = 0x100, + PAGEFILE_DISK = 0x200, + HIBERNATIONFILE_DISK = 0x400, + CRASHDUMP_DISK = 0x800, + HAS_ARC_PATH = 0x1000, + DYNAMIC = 0x2000, + BOOT_FROM_DISK = 0x4000, + CURRENT_READ_ONLY = 0x8000 + } + + public enum VDS_DISK_STATUS + { + UNKNOWN = 0x00000000, + ONLINE = 0x00000001, + NOT_READY = 0x00000002, + NO_MEDIA = 0x00000003, + OFFLINE = 0x00000004, + FAILED = 0x00000005, + MISSING = 0x00000006 + } + + public enum VDS_DEVICE_TYPE : uint + { + CD_ROM = 0x00000002, + DISK = 0x00000007, + DVD = 0x00000033 + } + + public enum VDS_MEDIA_TYPE : uint + { + Unknown = 0x00000000, + RemovableMedia = 0x0000000B, + FixedMedia = 0x0000000C + } + + public enum VDS_HEALTH : uint + { + UNKNOWN = 0x00000000, + HEALTHY = 0x00000001, + REBUILDING = 0x00000002, + STALE = 0x00000003, + FAILING = 0x00000004, + FAILING_REDUNDANCY = 0x00000005, + FAILED_REDUNDANCY = 0x00000006, + FAILED_REDUNDANCY_FAILING = 0x00000007, + FAILED = 0x00000008 + } + + public enum VDS_LUN_RESERVE_MODE : uint + { + NONE = 0x00000000, + EXCLUSIVE_RW = 0x00000001, + EXCLUSIVE_RO = 0x00000002, + SHARED_RO = 0x00000003, + SHARED_RW = 0x00000004 + } + + public enum VDS_DRIVE_LETTER_FLAG : uint + { + NON_PERSISTENT = 0x1 + } + + public enum VDS_QUERY_PROVIDER_FLAG : uint + { + SOFTWARE_PROVIDERS = 0x1, + HARDWARE_PROVIDERS = 0x2, + VIRTUALDISK_PROVIDERS = 0x4 + } + + [Flags] + public enum VDS_GPT_ATTRIBUTES : ulong + { + /// + /// Partition is required for the platform to function properly + /// + ATTRIBUTE_PLATFORM_REQUIRED = 0x0000000000000001, + /// + /// Partition cannot be written to but can be read from. Used only with the basic data partition type + /// + BASIC_DATA_ATTRIBUTE_READ_ONLY = 0x1000000000000000, + /// + /// Partition is a shadow copy. Used only with the basic data partition type. + /// + BASIC_DATA_ATTRIBUTE_SHADOW_COPY = 0x2000000000000000, + /// + /// Partition is hidden and will not be mounted. Used only with the basic data partition type. + /// + BASIC_DATA_ATTRIBUTE_HIDDEN = 0x4000000000000000, + /// + /// Partition does not receive a drive letter by default when moving the disk to another machine. Used only with the basic data partition type. + /// + BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER = 0x8000000000000000 + } + + [Flags] + public enum VDS_FILE_SYSTEM_PROP_FLAG : uint + { + COMPRESSED = 0x00000001 + } +} diff --git a/client/data/Methods/VDS/Interfaces.cs b/client/data/Methods/VDS/Interfaces.cs new file mode 100644 index 0000000..2bc53b6 --- /dev/null +++ b/client/data/Methods/VDS/Interfaces.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.InteropServices; + +namespace specify_client.Data.Methods.VDS +{ + [ComImport, Guid("E0393303-90D4-4A97-AB71-E9B671EE2729"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsServiceLoader + { + [PreserveSig] int LoadService([In][MarshalAs(UnmanagedType.LPWStr)] string machineName, [Out] out IVdsService service); + } + + [ComImport, Guid("0818A8EF-9BA9-40D8-A6F9-E22833CC771E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsService + { + void IsServiceReady(); + [PreserveSig] int WaitForServiceReady(); + void GetProperties(); + [PreserveSig] int QueryProviders([In] VDS_QUERY_PROVIDER_FLAG masks, [Out] out IEnumVdsObject ppEnum); + void QueryMaskedDisks(); + void QueryUnallocatedDisks(out IEnumVdsObject ppEnum); + void GetObject(); + [PreserveSig] int QueryDriveLetters([In]ushort wcFirstLetter, [In] int count, [In, Out, MarshalAs(UnmanagedType.LPArray,SizeParamIndex = 1)] VDS_DRIVE_LETTER_PROP[] array); + } + + [ComImport, Guid("118610B7-8D94-4030-B5B8-500889788E4E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumVdsObject + { + [PreserveSig] int Next([In] uint celt, [MarshalAs(UnmanagedType.IUnknown)][Out] out object ppObjectArray, [Out] out int pcFetched); + } + + [ComImport, Guid("3b69d7f5-9d94-4648-91ca-79939ba263bf"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsPack + { + void GetProperties(); + void GetProvider(); + [PreserveSig] int QueryVolumes(out IEnumVdsObject volumesEnumerator); + [PreserveSig] int QueryDisks(out IEnumVdsObject disksEnumerator); + + } + + [ComImport, Guid("07e5c822-f00c-47a1-8fce-b244da56fd06"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsDisk + { + [PreserveSig] + int GetProperties(out VDS_DISK_PROP properties); + void GetPack(); + void GetIdentificationData(); + + [PreserveSig] + int QueryExtents([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out VDS_DISK_EXTENT[] ppExtentArray, out int plNumberOfExtents); + } + + [ComImport, Guid("10C5E575-7984-4E81-A56B-431F5F92AE42"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsProvider + { + } + + [ComImport, Guid("9aa58360-ce33-4f92-b658-ed24b14425b8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsSwProvider + { + [PreserveSig] + int QueryPacks(out IEnumVdsObject packEnumerator); + + [PreserveSig] + int CreatePack(out IVdsPack pack); + } + + [ComImport, Guid("88306bb2-e71f-478c-86a2-79da200a0f11"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsVolume + { + [PreserveSig] + int GetProperties(out VDS_VOLUME_PROP properties); + + [PreserveSig] + int GetPack(out IVdsPack pack); + + [PreserveSig] + int QueryPlexes(out IEnumVdsObject plexEnumerator); + } + + [ComImport, Guid("EE2D5DED-6236-4169-931D-B9778CE03DC6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsVolumeMF + { + [PreserveSig] + int GetFileSystemProperties(out VDS_FILE_SYSTEM_PROP fileSystemProp); + } + + [ComImport, Guid("4DAA0135-E1D1-40F1-AAA5-3CC1E53221C3"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsVolumePlex + { + void GetProperties(); + void GetVolume(); + [PreserveSig] int QueryExtents([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out VDS_DISK_EXTENT[] extents, out int numberOfExtents); + void Repair(); + } + + [ComImport, Guid("3858C0D5-0F35-4BF5-9714-69874963BC36"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVdsAdvancedDisk3 + { + [PreserveSig] int GetProperties(out VDS_ADVANCEDDISK_PROP advDiskprop); + } +} diff --git a/client/data/Methods/VDS/Structs.cs b/client/data/Methods/VDS/Structs.cs new file mode 100644 index 0000000..049b56d --- /dev/null +++ b/client/data/Methods/VDS/Structs.cs @@ -0,0 +1,104 @@ +using System; +using System.Runtime.InteropServices; + +namespace specify_client.Data.Methods.VDS +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct VDS_DRIVE_LETTER_PROP + { + public char wcLetter; + public Guid volumeId; + public VDS_DRIVE_LETTER_FLAG ulFlags; + public bool bUsed; + } + + [StructLayout(LayoutKind.Sequential)] + public struct VDS_DISK_PROP + { + public Guid Id; + public VDS_DISK_STATUS Status; + public VDS_LUN_RESERVE_MODE ReserveMode; + public VDS_HEALTH Health; + public VDS_DEVICE_TYPE DeviceType; + public VDS_MEDIA_TYPE MediaType; + public ulong Size; + public uint BytesPerSector; + public uint SectorsPerTrack; + public uint TracksPerCylinder; + public VDS_DISK_FLAG Flags; + public VDS_STORAGE_BUS_TYPE BusType; + public VDS_PARTITION_STYLE PartitionStyle; + + public Guid DiskGuid; + [MarshalAs(UnmanagedType.LPWStr)] public string DiskAddress; + [MarshalAs(UnmanagedType.LPWStr)] public string Name; + [MarshalAs(UnmanagedType.LPWStr)] public string FriendlyName; + [MarshalAs(UnmanagedType.LPWStr)] public string AdaptorName; + [MarshalAs(UnmanagedType.LPWStr)] public string DevicePath; + } + + [StructLayout(LayoutKind.Sequential)] + public struct VDS_ADVANCEDDISK_PROP + { + [MarshalAs(UnmanagedType.LPWStr)] public string Id; + [MarshalAs(UnmanagedType.LPWStr)] public string Pathname; + [MarshalAs(UnmanagedType.LPWStr)] public string Location; + [MarshalAs(UnmanagedType.LPWStr)] public string FriendlyName; + [MarshalAs(UnmanagedType.LPWStr)] public string Identifier; + public ushort IdentifierFormat; + public uint Number; + [MarshalAs(UnmanagedType.LPWStr)] public string SerialNumber; + [MarshalAs(UnmanagedType.LPWStr)] public string FirmwareVersion; + [MarshalAs(UnmanagedType.LPWStr)] public string Manufacturer; + [MarshalAs(UnmanagedType.LPWStr)] public string Model; + public ulong TotalSize; + public ulong AllocatedSize; + public uint LogicalSectorSize; + public uint PhysicalSectorSize; + public uint PartitionCount; + public VDS_DISK_STATUS Status; + public VDS_HEALTH Health; + public VDS_STORAGE_BUS_TYPE BusType; + public VDS_PARTITION_STYLE PartitionStyle; + public Guid DiskGuid; + public VDS_DISK_FLAG Flags; + public VDS_DEVICE_TYPE DeviceType; + } + + public struct VDS_FILE_SYSTEM_PROP + { + public VDS_FILE_SYSTEM_TYPE Type; + public Guid VolumeId; + public VDS_FILE_SYSTEM_PROP_FLAG Flags; + public ulong TotalAllocationUnits; + public ulong AvailableAllocationUnits; + public uint AllocationUnitSize; + [MarshalAs(UnmanagedType.LPWStr)] public string Label; + } + + [StructLayout(LayoutKind.Sequential)] + public struct VDS_VOLUME_PROP + { + public Guid Id; + public VDS_VOLUME_TYPE Type; + public VDS_VOLUME_STATUS Status; + public VDS_HEALTH Health; + public VDS_TRANSITION_STATE TransitionState; + public ulong Size; + public VDS_VOLUME_FLAG Flags; + public VDS_FILE_SYSTEM_TYPE RecommendedFileSystemType; + [MarshalAs(UnmanagedType.LPWStr)] public string Name; + } + + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 80)] + public struct VDS_DISK_EXTENT + { + [FieldOffset(0)] public Guid diskId; + [FieldOffset(16)] public VDS_DISK_EXTENT_TYPE type; + [FieldOffset(24)] public ulong Offset; + [FieldOffset(32)] public ulong Size; + [FieldOffset(40)] public Guid volumeId; + [FieldOffset(56)] public Guid plexId; + [FieldOffset(72)] public uint memberIndex; + } +} diff --git a/client/data/Methods/VDS/VdsClass.cs b/client/data/Methods/VDS/VdsClass.cs new file mode 100644 index 0000000..8e7ae9f --- /dev/null +++ b/client/data/Methods/VDS/VdsClass.cs @@ -0,0 +1,253 @@ +using specify_client.data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows.Media; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrackBar; + +namespace specify_client.Data.Methods.VDS +{ + public static class VDSClass + { + //COM HResults + public const uint HR_PROPERTIES_INCOMPLETE = 0x00042715u; + + private const int DRIVE_LETTER_COUNT = 26; + public static IVdsService Create() + { + var vdsLoader = (IVdsServiceLoader)new VdsServiceLoader(); + int hr = vdsLoader.LoadService(null, out IVdsService vdsService); + Marshal.ThrowExceptionForHR(hr); + + hr = vdsService.WaitForServiceReady(); + Marshal.ThrowExceptionForHR(hr); + + return vdsService; + } + + public static List GetDisksInfo() + { + List drives = new(); + + int hr; + + IVdsService vdsService = Create(); + + VDS_DRIVE_LETTER_PROP[] driveLetters = new VDS_DRIVE_LETTER_PROP[DRIVE_LETTER_COUNT]; + + var packs = GetPacks(vdsService); + + var vdsDisks = GetDisks(packs); + var vdsVolumes = GetVolumes(packs); + hr = vdsService.QueryDriveLetters('A', 26, driveLetters); + if (!(hr == 0 || hr == HR_PROPERTIES_INCOMPLETE)) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + } + + foreach (var vdsDisk in vdsDisks) + { + DiskDrive disk; + if (vdsDisk is IVdsAdvancedDisk3 advDisk3) // May not cast, if disk is a dynamic disk + { + hr = advDisk3.GetProperties(out VDS_ADVANCEDDISK_PROP advDiskProps); // VDS_ADVANCEDDISK_PROP exposes more data than VDS_DISK_PROP + if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + + disk = new DiskDrive(); + disk.DiskCapacity = advDiskProps.TotalSize; + disk.DeviceName = string.Copy(advDiskProps.FriendlyName ?? string.Empty); + disk.SerialNumber = string.Copy(advDiskProps.SerialNumber ?? string.Empty); + disk.PartitionScheme = Enum.GetName(typeof(VDS_PARTITION_STYLE), advDiskProps.PartitionStyle); + disk.InstanceId = advDiskProps.DiskGuid.ToString(); + disk.InterfaceType = Enum.GetName(typeof(VDS_STORAGE_BUS_TYPE),advDiskProps.BusType); + disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), advDiskProps.DeviceType); + disk.BlockSize = advDiskProps.LogicalSectorSize; + disk.DiskNumber = (uint)drives.Count; + + hr = vdsDisk.GetProperties(out VDS_DISK_PROP diskProps); + if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), diskProps.MediaType); + } + else + { + hr = vdsDisk.GetProperties(out VDS_DISK_PROP diskProps); + if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + + disk = new DiskDrive(); + disk.DiskCapacity = diskProps.Size; + disk.DeviceName = string.Copy(diskProps.FriendlyName ?? string.Empty); + disk.SerialNumber = string.Empty; // VDS_DISK_PROP doesnt expose serial number + disk.PartitionScheme = Enum.GetName(typeof(VDS_PARTITION_STYLE), diskProps.PartitionStyle); + disk.InstanceId = diskProps.DiskGuid.ToString(); + disk.InterfaceType = Enum.GetName(typeof(VDS_STORAGE_BUS_TYPE), diskProps.BusType); + disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), diskProps.MediaType); + disk.BlockSize = diskProps.BytesPerSector; + disk.DiskNumber = (uint)drives.Count; + } + + hr = vdsDisk.QueryExtents(out VDS_DISK_EXTENT[] extents, out _); + if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + + + disk.Partitions = extents.Select(x => new Partition() + { + PartitionOffset = x.Offset, + DeviceId = x.diskId.ToString(), + PartitionCapacity = x.Size, + ExtentType = Enum.GetName(typeof(VDS_DISK_EXTENT_TYPE), x.type) + }).ToList(); + + drives.Add(disk); + } + + foreach (var volume in vdsVolumes) + { + List vdsExtents = new List(); + VDS_FILE_SYSTEM_PROP vdsFileSystemProp = new VDS_FILE_SYSTEM_PROP(); + + hr = volume.GetProperties(out VDS_VOLUME_PROP volumeProps); + if (hr != 0 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + + var matchingPartitions = drives.SelectMany(x => x.Partitions).Where(x => x.DeviceId == volumeProps.Id.ToString()); + + foreach (var plex in volume.GetVolumePlexes()) + { + hr = plex.QueryExtents(out VDS_DISK_EXTENT[] extents, out _); + if (hr != 0) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + + vdsExtents.AddRange(extents); + } + + + if (volume is IVdsVolumeMF volumeMf) + { + hr = volumeMf.GetFileSystemProperties(out VDS_FILE_SYSTEM_PROP fileSystemProp); + if (hr != 0 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + } + + vdsFileSystemProp = fileSystemProp; + } + + foreach (var partition in drives.SelectMany(x => x.Partitions).Where(x => vdsExtents.Any(y => y.Offset == x.PartitionOffset && y.diskId.ToString() == x.DeviceId))) + { + partition.VolumeId = volumeProps.Id.ToString(); + partition.DirtyBitSet = (volumeProps.Flags ^ VDS_VOLUME_FLAG.DIRTY) == VDS_VOLUME_FLAG.DIRTY; + partition.Filesystem = Enum.GetName(typeof(VDS_FILE_SYSTEM_TYPE), vdsFileSystemProp.Type); + partition.PartitionLabel = string.Copy(vdsFileSystemProp.Label ?? string.Empty); + partition.PartitionCapacity = volumeProps.Size; + partition.PartitionFree = vdsFileSystemProp.AllocationUnitSize * vdsFileSystemProp.AvailableAllocationUnits; + partition.VolumeType = Enum.GetName(typeof(VDS_VOLUME_TYPE), volumeProps.Type); + } + + } + + foreach (var driveLetter in driveLetters.Where(x => x.bUsed)) + { + var partition = drives.SelectMany(x => x.Partitions).FirstOrDefault(x => x.VolumeId == driveLetter.volumeId.ToString()); + if (partition != null) + { + partition.PartitionLetter = driveLetter.wcLetter.ToString(); + } + } + + foreach (var drive in drives) + { + drive.DiskFree = (ulong)drive.Partitions.Sum(x => (long)x.PartitionFree); + } + + return drives; + + } + + [ComImport, Guid("9c38ed61-d565-4728-aeee-c80952f0ecde")] + public class VdsServiceLoader + { + + } + + private static IEnumerable GetDisks(IEnumerable vdsPacks) + { + foreach (var pack in vdsPacks) + { + Marshal.ThrowExceptionForHR(pack.QueryDisks(out IEnumVdsObject diskEnum)); + while (0 == diskEnum.Next(1, out object iface, out _)) + { + yield return iface as IVdsDisk; + } + } + } + + private static IEnumerable GetVolumes(IEnumerable vdsPacks) + { + foreach (var pack in vdsPacks) + { + pack.QueryVolumes(out IEnumVdsObject volumeEnum); + while (0 == volumeEnum.Next(1, out object iface, out _)) + { + yield return iface as IVdsVolume; + } + } + } + + private static IEnumerable GetVolumePlexes(this IVdsVolume pack) + { + Marshal.ThrowExceptionForHR(pack.QueryPlexes(out IEnumVdsObject plexEnum)); + while (0 == plexEnum.Next(1, out object iface, out _)) + { + yield return iface as IVdsVolumePlex; + } + } + + private static IEnumerable GetProviders(IVdsService vdsService, VDS_QUERY_PROVIDER_FLAG providerMask = VDS_QUERY_PROVIDER_FLAG.SOFTWARE_PROVIDERS) + { + Marshal.ThrowExceptionForHR(vdsService.QueryProviders(providerMask, out IEnumVdsObject vEnum)); + while (0 == vEnum.Next(1, out object iface, out _)) + { + yield return iface as IVdsProvider; + } + } + + private static IEnumerable GetPacks(IVdsService vdsService) + { + foreach (IVdsSwProvider swProvider in GetProviders(vdsService,VDS_QUERY_PROVIDER_FLAG.SOFTWARE_PROVIDERS).OfType()) + { + if (swProvider == null) + continue; + + Marshal.ThrowExceptionForHR(swProvider.QueryPacks(out IEnumVdsObject vEnum)); + while (0 == vEnum.Next(1, out object iface, out _)) + { + yield return iface as IVdsPack; + } + } + } + + } +} diff --git a/client/data/Structs.cs b/client/data/Structs.cs index 8a996d6..d9a2b5b 100644 --- a/client/data/Structs.cs +++ b/client/data/Structs.cs @@ -87,16 +87,20 @@ public class DiskDrive public class Partition { + public ulong PartitionOffset; public ulong PartitionCapacity; public ulong PartitionFree; public string PartitionLabel; public string PartitionLetter; + public string VolumeType; + public string ExtentType; public string Filesystem; public uint CfgMgrErrorCode; public uint LastErrorCode; public bool DirtyBitSet; public bool BitlockerEncryptionStatus = false; [NonSerialized()] public string DeviceId; // Only used to link partitions, do not serialize. + [NonSerialized()] public string VolumeId; } public class SmartAttribute diff --git a/client/specify_client.csproj b/client/specify_client.csproj index e5f2b6a..da6149b 100644 --- a/client/specify_client.csproj +++ b/client/specify_client.csproj @@ -144,6 +144,10 @@ + + + + From 329277e66a401394dacf2b067b0bd4581de95652 Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Fri, 4 Oct 2024 01:10:19 +0200 Subject: [PATCH 2/8] Fix missing dynamic drives from queries. Since we get VdsDiskProperties anyway, we can simplify the logic. --- client/data/Methods/VDS/VdsClass.cs | 63 +++++++++++------------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/client/data/Methods/VDS/VdsClass.cs b/client/data/Methods/VDS/VdsClass.cs index 8e7ae9f..9da7287 100644 --- a/client/data/Methods/VDS/VdsClass.cs +++ b/client/data/Methods/VDS/VdsClass.cs @@ -48,55 +48,40 @@ public static List GetDisksInfo() foreach (var vdsDisk in vdsDisks) { - DiskDrive disk; + DiskDrive disk = new DiskDrive(); + + hr = vdsDisk.GetProperties(out VDS_DISK_PROP diskProps); + if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) + { + Console.WriteLine(Marshal.GetExceptionForHR(hr)); + continue; + } + + disk = new DiskDrive(); + disk.DiskCapacity = diskProps.Size; + disk.DeviceName = string.Copy(diskProps.FriendlyName ?? string.Empty); + disk.SerialNumber = string.Empty; // VDS_DISK_PROP doesnt expose serial number + disk.PartitionScheme = Enum.GetName(typeof(VDS_PARTITION_STYLE), diskProps.PartitionStyle); + disk.InstanceId = diskProps.DiskGuid.ToString(); + disk.InterfaceType = Enum.GetName(typeof(VDS_STORAGE_BUS_TYPE), diskProps.BusType); + disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), diskProps.MediaType); + disk.BlockSize = diskProps.BytesPerSector; + disk.DiskNumber = (uint)drives.Count; + if (vdsDisk is IVdsAdvancedDisk3 advDisk3) // May not cast, if disk is a dynamic disk { hr = advDisk3.GetProperties(out VDS_ADVANCEDDISK_PROP advDiskProps); // VDS_ADVANCEDDISK_PROP exposes more data than VDS_DISK_PROP if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) { Console.WriteLine(Marshal.GetExceptionForHR(hr)); - continue; } - - disk = new DiskDrive(); - disk.DiskCapacity = advDiskProps.TotalSize; - disk.DeviceName = string.Copy(advDiskProps.FriendlyName ?? string.Empty); - disk.SerialNumber = string.Copy(advDiskProps.SerialNumber ?? string.Empty); - disk.PartitionScheme = Enum.GetName(typeof(VDS_PARTITION_STYLE), advDiskProps.PartitionStyle); - disk.InstanceId = advDiskProps.DiskGuid.ToString(); - disk.InterfaceType = Enum.GetName(typeof(VDS_STORAGE_BUS_TYPE),advDiskProps.BusType); - disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), advDiskProps.DeviceType); - disk.BlockSize = advDiskProps.LogicalSectorSize; - disk.DiskNumber = (uint)drives.Count; - - hr = vdsDisk.GetProperties(out VDS_DISK_PROP diskProps); - if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) + else { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); - continue; + disk.SerialNumber = string.Copy(advDiskProps.SerialNumber ?? string.Empty); } - disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), diskProps.MediaType); } - else - { - hr = vdsDisk.GetProperties(out VDS_DISK_PROP diskProps); - if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) - { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); - continue; - } - disk = new DiskDrive(); - disk.DiskCapacity = diskProps.Size; - disk.DeviceName = string.Copy(diskProps.FriendlyName ?? string.Empty); - disk.SerialNumber = string.Empty; // VDS_DISK_PROP doesnt expose serial number - disk.PartitionScheme = Enum.GetName(typeof(VDS_PARTITION_STYLE), diskProps.PartitionStyle); - disk.InstanceId = diskProps.DiskGuid.ToString(); - disk.InterfaceType = Enum.GetName(typeof(VDS_STORAGE_BUS_TYPE), diskProps.BusType); - disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), diskProps.MediaType); - disk.BlockSize = diskProps.BytesPerSector; - disk.DiskNumber = (uint)drives.Count; - } + drives.Add(disk); hr = vdsDisk.QueryExtents(out VDS_DISK_EXTENT[] extents, out _); if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) @@ -113,8 +98,6 @@ public static List GetDisksInfo() PartitionCapacity = x.Size, ExtentType = Enum.GetName(typeof(VDS_DISK_EXTENT_TYPE), x.type) }).ToList(); - - drives.Add(disk); } foreach (var volume in vdsVolumes) From b0ebb5ff6a325f6b09636a0c0d36cf27b9f2648f Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Wed, 9 Oct 2024 22:46:08 +0200 Subject: [PATCH 3/8] Optimise getting partition by drive letter --- client/data/Methods/Hardware.cs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/client/data/Methods/Hardware.cs b/client/data/Methods/Hardware.cs index 5cb5852..5d3a1dd 100644 --- a/client/data/Methods/Hardware.cs +++ b/client/data/Methods/Hardware.cs @@ -859,25 +859,18 @@ private static List GetBitlockerStatus(List drives) } return drives; } + private static Partition GetPartitionByDriveLetter(string driveLetter, List drives) { - foreach (var drive in drives) + Partition part = drives?.SelectMany(x => x.Partitions).Where(x=> !string.IsNullOrWhiteSpace(x?.PartitionLetter)).FirstOrDefault(x => driveLetter.StartsWith(x.PartitionLetter, StringComparison.CurrentCultureIgnoreCase)); + + if (part == null) { - foreach (var partition in drive.Partitions) - { - if (string.IsNullOrEmpty(partition.PartitionLetter)) - { - continue; - } - if (partition.PartitionLetter.Equals(driveLetter)) - { - return partition; - } - } - } LogEvent($"A matching partition could not be found for drive letter \"{driveLetter}\"", Region.Hardware, EventType.ERROR); - return null; } + return part; + } + private static List GetPartitionSchemes(List drives) { foreach (var drive in drives) From 7c53a69b88a0b7986219374c6d85ea29ea21265c Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Wed, 9 Oct 2024 22:54:32 +0200 Subject: [PATCH 4/8] Move free space calculation to new method, as calculations are done in VDS method. If VDS fails, use old method as backup. --- client/data/Methods/Hardware.cs | 34 ++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/client/data/Methods/Hardware.cs b/client/data/Methods/Hardware.cs index 5d3a1dd..083416e 100644 --- a/client/data/Methods/Hardware.cs +++ b/client/data/Methods/Hardware.cs @@ -927,14 +927,29 @@ private static List GetPartitionSchemes(List drives) } private static async Task GetDiskDriveData() { + List drives = null; + try + { + // First try getting drive information from VDS service + drives = Data.Methods.VDS.VDSClass.GetDisksInfo(); + } + catch (Exception ex) + { + LogEvent($"Failed to get disk information from VDS service. Error: {ex.Message}", Region.Hardware, EventType.ERROR); + } + + // If VDS method fails, get back to old methods + if (drives == null) + { // "Basic" in this context refers to data we can retrieve directly from WMI without much processing. Model names, partition labels, etc. - //List drives = GetBasicDriveInfo(); - //drives = GetBasicPartitionInfo(drives); - //drives = LinkLogicalPartitions(drives); - //drives = LinkNonLogicalPartitions(drives); - //drives = GetPartitionSchemes(drives); - List drives = Data.Methods.VDS.VDSClass.GetDisksInfo(); + drives = GetBasicDriveInfo(); + drives = GetBasicPartitionInfo(drives); + drives = LinkLogicalPartitions(drives); + drives = LinkNonLogicalPartitions(drives); + drives = GetPartitionSchemes(drives); drives = GetBitlockerStatus(drives); + drives = GetDiskFreeSpace(drives); + } try { @@ -971,6 +986,11 @@ private static async Task GetDiskDriveData() } } + Disks = drives; + } + + private static List GetDiskFreeSpace(List drives) + { foreach (var d in drives) { bool complete = true; @@ -1012,7 +1032,7 @@ private static async Task GetDiskDriveData() d.DiskFree = free; } } - Disks = drives; + return drives; } private static DiskDrive GetNvmeSmart(DiskDrive drive) From 012b28b718b08dfb8412faaa48b91fea0262f090 Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Wed, 9 Oct 2024 23:58:19 +0200 Subject: [PATCH 5/8] Adapt NVMe smart data for VDS method. TODO: Smart data for non-NVMe drives. --- client/data/Methods/Hardware.cs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/client/data/Methods/Hardware.cs b/client/data/Methods/Hardware.cs index 083416e..8bbc2f3 100644 --- a/client/data/Methods/Hardware.cs +++ b/client/data/Methods/Hardware.cs @@ -969,14 +969,11 @@ private static async Task GetDiskDriveData() LogEvent($"{e}", Region.Hardware); } - for (int i = 0; i < drives.Count; i++) + foreach (var drive in drives.Where(x => x.SmartData == null)) { - var drive = drives[i]; - if (drive.SmartData == null) - { try { - drive = GetNvmeSmart(drive); + GetNvmeSmart(drive); } catch (Exception e) { @@ -984,7 +981,6 @@ private static async Task GetDiskDriveData() LogEvent($"{e}", Region.Hardware); } } - } Disks = drives; } @@ -1038,21 +1034,14 @@ private static List GetDiskFreeSpace(List drives) private static DiskDrive GetNvmeSmart(DiskDrive drive) { // Stop if drive is not an NVME drive. This happens on all external drives. - if(drive.InterfaceType != "SCSI" || !drive.MediaType.ToLower().Contains("fixed")) + if (!drive.InterfaceType.Equals("NVMe", StringComparison.CurrentCultureIgnoreCase) || //VDS check + (drive.InterfaceType != "SCSI" || !drive.MediaType.ToLower().Contains("fixed"))) //Old method check { LogEvent($"Could not retrieve NVME Smart Data. Drive {drive.DeviceName} is not an NVME drive. Interface: {drive.InterfaceType}. Media type: {drive.MediaType}", Region.Hardware); return drive; } - // Get the drive letter to send to CreateFile() - string driveLetter = ""; - foreach (var partition in drive.Partitions) - { - if (partition.PartitionLetter != null && partition.PartitionLetter.Length == 2) - { - driveLetter = partition.PartitionLetter; - break; - } - } + + string driveLetter = drive.Partitions?.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.PartitionLetter))?.PartitionLetter; // If no drive letter was found, it is impossible to obtain a valid handle. if (string.IsNullOrEmpty(driveLetter)) @@ -1060,6 +1049,8 @@ private static DiskDrive GetNvmeSmart(DiskDrive drive) LogEvent($"Attempted to gather smart data from unlettered drive. {drive.DeviceName}", Region.Hardware, EventType.WARNING); return drive; } + driveLetter = driveLetter.EndsWith(":") ? driveLetter : $"{driveLetter}:"; + // Find a valid handle. driveLetter = $@"\\.\{driveLetter}" + '\0'; From 0b310b79ada92d6dc862777cba37139f73ec3203 Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Tue, 15 Oct 2024 21:14:51 +0200 Subject: [PATCH 6/8] Fix retrieving bitlocker and nvme smart data from VDS method. Add getting DeviceId (Name) from WMI --- client/data/Methods/Hardware.cs | 44 ++++++++++++++++------------- client/data/Methods/VDS/VdsClass.cs | 1 + client/data/Structs.cs | 1 + 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/client/data/Methods/Hardware.cs b/client/data/Methods/Hardware.cs index 8bbc2f3..33d16c1 100644 --- a/client/data/Methods/Hardware.cs +++ b/client/data/Methods/Hardware.cs @@ -516,6 +516,14 @@ private static List GetBasicDriveInfo() { drive.DeviceName = drive.DeviceName.Trim(); } + if (!driveWmi.TryWmiRead("Name", out drive.DeviceId)) + { + LogEvent($"Could not retrieve device id of drive @ index {diskNumber}", Region.Hardware, EventType.ERROR); + } + else + { + drive.DeviceId = drive.DeviceId.Trim(); + } if (!driveWmi.TryWmiRead("SerialNumber", out drive.SerialNumber)) { LogEvent($"Could not retrieve serial number of drive @ index {diskNumber}", Region.Hardware, EventType.ERROR); @@ -932,6 +940,14 @@ private static async Task GetDiskDriveData() { // First try getting drive information from VDS service drives = Data.Methods.VDS.VDSClass.GetDisksInfo(); + + // Get device PnP ID from WMI, VDS does not provide them. + var wmidrives = GetBasicDriveInfo(); + + foreach (var drive in drives) + { + drive.InstanceId = wmidrives.FirstOrDefault(x => x.DeviceId.Equals(drive.DeviceId.Replace('?','.'),StringComparison.CurrentCultureIgnoreCase))?.InstanceId; + } } catch (Exception ex) { @@ -947,10 +963,11 @@ private static async Task GetDiskDriveData() drives = LinkLogicalPartitions(drives); drives = LinkNonLogicalPartitions(drives); drives = GetPartitionSchemes(drives); - drives = GetBitlockerStatus(drives); drives = GetDiskFreeSpace(drives); } + drives = GetBitlockerStatus(drives); + try { var queryCollection = GetWmi("MSStorageDriver_FailurePredictData", "*", "\\\\.\\root\\wmi"); @@ -1034,32 +1051,20 @@ private static List GetDiskFreeSpace(List drives) private static DiskDrive GetNvmeSmart(DiskDrive drive) { // Stop if drive is not an NVME drive. This happens on all external drives. - if (!drive.InterfaceType.Equals("NVMe", StringComparison.CurrentCultureIgnoreCase) || //VDS check + if (!drive.InterfaceType.Equals("NVMe", StringComparison.CurrentCultureIgnoreCase) && //VDS check (drive.InterfaceType != "SCSI" || !drive.MediaType.ToLower().Contains("fixed"))) //Old method check { LogEvent($"Could not retrieve NVME Smart Data. Drive {drive.DeviceName} is not an NVME drive. Interface: {drive.InterfaceType}. Media type: {drive.MediaType}", Region.Hardware); return drive; } - string driveLetter = drive.Partitions?.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.PartitionLetter))?.PartitionLetter; - - // If no drive letter was found, it is impossible to obtain a valid handle. - if (string.IsNullOrEmpty(driveLetter)) - { - LogEvent($"Attempted to gather smart data from unlettered drive. {drive.DeviceName}", Region.Hardware, EventType.WARNING); - return drive; - } - driveLetter = driveLetter.EndsWith(":") ? driveLetter : $"{driveLetter}:"; - - - // Find a valid handle. - driveLetter = $@"\\.\{driveLetter}" + '\0'; - var handle = CreateFile(driveLetter, 0x40000000, 0x1 | 0x2, IntPtr.Zero, 0x3, 0, IntPtr.Zero); + // We can use DeviceId (Name in Windows) as a valid path to CreateFile (ex. \\?\PhysicalDrive0) instead of drive letter + var handle = CreateFile(drive.DeviceId, 0x40000000, 0x1 | 0x2, IntPtr.Zero, 0x3, 0, IntPtr.Zero); // Verify the handle. if (handle == new IntPtr(-1)) { - LogEvent($"NVMe Smart Data could not be retrieved. Invalid Handle. {driveLetter}", Region.Hardware, EventType.ERROR); + LogEvent($"NVMe Smart Data could not be retrieved. Invalid Handle. {drive.DeviceId}", Region.Hardware, EventType.ERROR); LogEvent($"Interop Error: {new Win32Exception(Marshal.GetLastWin32Error()).Message}", Region.Hardware); return drive; } @@ -1115,7 +1120,7 @@ private static DiskDrive GetNvmeSmart(DiskDrive drive) if (!result) { string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; - LogEvent($"Interop failure during NVMe SMART data retrieval. {Marshal.GetLastWin32Error()} - {errorMessage} on drive {driveLetter}", Region.Hardware, EventType.ERROR); + LogEvent($"Interop failure during NVMe SMART data retrieval. {Marshal.GetLastWin32Error()} - {errorMessage} on drive {drive.DiskNumber}", Region.Hardware, EventType.ERROR); Marshal.FreeHGlobal(buffer); return drive; } @@ -1128,7 +1133,7 @@ private static DiskDrive GetNvmeSmart(DiskDrive drive) var driveTemperature = ((uint)smartInfo->Temperature[1] << 8 | smartInfo->Temperature[0]) - 273; if (driveTemperature > 100) { - LogEvent($"SMART data retrieval error - Data not valid on drive {driveLetter}", Region.Hardware, EventType.ERROR); + LogEvent($"SMART data retrieval error - Data not valid on drive {drive.DiskNumber}", Region.Hardware, EventType.ERROR); Marshal.FreeHGlobal(buffer); return drive; } @@ -1183,6 +1188,7 @@ private static DiskDrive GetNvmeSmart(DiskDrive drive) Marshal.FreeHGlobal(buffer); return drive; } + private unsafe static SmartAttribute MakeNvmeAttribute(byte* attr, byte id, string name) { unsafe diff --git a/client/data/Methods/VDS/VdsClass.cs b/client/data/Methods/VDS/VdsClass.cs index 9da7287..62ee913 100644 --- a/client/data/Methods/VDS/VdsClass.cs +++ b/client/data/Methods/VDS/VdsClass.cs @@ -67,6 +67,7 @@ public static List GetDisksInfo() disk.MediaType = Enum.GetName(typeof(VDS_MEDIA_TYPE), diskProps.MediaType); disk.BlockSize = diskProps.BytesPerSector; disk.DiskNumber = (uint)drives.Count; + disk.DeviceId = string.Copy(diskProps.Name ?? string.Empty); if (vdsDisk is IVdsAdvancedDisk3 advDisk3) // May not cast, if disk is a dynamic disk { diff --git a/client/data/Structs.cs b/client/data/Structs.cs index d9a2b5b..b8feacf 100644 --- a/client/data/Structs.cs +++ b/client/data/Structs.cs @@ -83,6 +83,7 @@ public class DiskDrive public List SmartData; [NonSerialized()] public string InstanceId; // Only used to link SmartData, do not serialize. Unless you really want to. + [NonSerialized] public string DeviceId; // Used to get device PnP Id from VDS method and get nvme SmartData } public class Partition From 4870cb5e7eb07c0e119e54582fb2e8c53001b72b Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Tue, 15 Oct 2024 21:40:42 +0200 Subject: [PATCH 7/8] Replace console writes with logging methods --- client/data/Methods/VDS/VdsClass.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/client/data/Methods/VDS/VdsClass.cs b/client/data/Methods/VDS/VdsClass.cs index 62ee913..ee837ee 100644 --- a/client/data/Methods/VDS/VdsClass.cs +++ b/client/data/Methods/VDS/VdsClass.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Windows.Media; +using static specify_client.DebugLog; using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrackBar; namespace specify_client.Data.Methods.VDS @@ -43,7 +44,7 @@ public static List GetDisksInfo() hr = vdsService.QueryDriveLetters('A', 26, driveLetters); if (!(hr == 0 || hr == HR_PROPERTIES_INCOMPLETE)) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get drive letters from VDS. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.ERROR); } foreach (var vdsDisk in vdsDisks) @@ -53,7 +54,7 @@ public static List GetDisksInfo() hr = vdsDisk.GetProperties(out VDS_DISK_PROP diskProps); if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get drive data from VDS. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.ERROR); continue; } @@ -74,7 +75,7 @@ public static List GetDisksInfo() hr = advDisk3.GetProperties(out VDS_ADVANCEDDISK_PROP advDiskProps); // VDS_ADVANCEDDISK_PROP exposes more data than VDS_DISK_PROP if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get advanced drive data from VDS. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.WARNING); // Warning, as it's only needed for serial number. For dynamic drives it's normal } else { @@ -87,7 +88,7 @@ public static List GetDisksInfo() hr = vdsDisk.QueryExtents(out VDS_DISK_EXTENT[] extents, out _); if (hr != 0 && hr != 1 && hr != HR_PROPERTIES_INCOMPLETE) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get advanced drive extend data from VDS for disk {disk.DeviceName}. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.ERROR); continue; } @@ -109,7 +110,7 @@ public static List GetDisksInfo() hr = volume.GetProperties(out VDS_VOLUME_PROP volumeProps); if (hr != 0 && hr != HR_PROPERTIES_INCOMPLETE) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get volume data from VDS. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.ERROR); continue; } @@ -120,7 +121,7 @@ public static List GetDisksInfo() hr = plex.QueryExtents(out VDS_DISK_EXTENT[] extents, out _); if (hr != 0) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get volume plexes from VDS for volume {volumeProps.Name}. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.ERROR); continue; } @@ -133,7 +134,7 @@ public static List GetDisksInfo() hr = volumeMf.GetFileSystemProperties(out VDS_FILE_SYSTEM_PROP fileSystemProp); if (hr != 0 && hr != HR_PROPERTIES_INCOMPLETE) { - Console.WriteLine(Marshal.GetExceptionForHR(hr)); + LogEvent($"Unable to get file system data from VDS. Exception: {Marshal.GetExceptionForHR(hr).Message}", Region.Hardware, EventType.ERROR); } vdsFileSystemProp = fileSystemProp; From 750f4dd7abd976631b458d4d8e06db7247507886 Mon Sep 17 00:00:00 2001 From: adam <1312adam@gmail.com> Date: Mon, 4 Nov 2024 22:03:20 +0100 Subject: [PATCH 8/8] Add drive letter for all extents in volume, not only first extent --- client/data/Methods/VDS/VdsClass.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/data/Methods/VDS/VdsClass.cs b/client/data/Methods/VDS/VdsClass.cs index ee837ee..305c632 100644 --- a/client/data/Methods/VDS/VdsClass.cs +++ b/client/data/Methods/VDS/VdsClass.cs @@ -155,8 +155,7 @@ public static List GetDisksInfo() foreach (var driveLetter in driveLetters.Where(x => x.bUsed)) { - var partition = drives.SelectMany(x => x.Partitions).FirstOrDefault(x => x.VolumeId == driveLetter.volumeId.ToString()); - if (partition != null) + foreach (var partition in drives.SelectMany(x => x.Partitions).Where(x => x.VolumeId == driveLetter.volumeId.ToString())) { partition.PartitionLetter = driveLetter.wcLetter.ToString(); } @@ -221,7 +220,7 @@ private static IEnumerable GetProviders(IVdsService vdsService, VD private static IEnumerable GetPacks(IVdsService vdsService) { - foreach (IVdsSwProvider swProvider in GetProviders(vdsService,VDS_QUERY_PROVIDER_FLAG.SOFTWARE_PROVIDERS).OfType()) + foreach (IVdsSwProvider swProvider in GetProviders(vdsService, VDS_QUERY_PROVIDER_FLAG.SOFTWARE_PROVIDERS).OfType()) { if (swProvider == null) continue;