diff --git a/SpecProbe/Hardware/Probers/HardDiskProber.cs b/SpecProbe/Hardware/Probers/HardDiskProber.cs index daab81c..c288df0 100644 --- a/SpecProbe/Hardware/Probers/HardDiskProber.cs +++ b/SpecProbe/Hardware/Probers/HardDiskProber.cs @@ -262,63 +262,101 @@ public BaseHardwarePartInfo[] GetBaseHardwarePartsWindows() try { - // First, get the list of logical partitions so that we can get the letters agnostically - var drives = DriveInfo.GetDrives(); - List<(string letter, long partSize)> parts = []; - foreach (var drive in drives) - { - if (drive.IsReady) - { - // Drive is ready. Now, check the type, as this prober only checks for HDD - if (drive.DriveType == DriveType.Fixed) - parts.Add(("\\\\.\\" + drive.Name[..(drive.Name.Length - 1)], drive.TotalSize)); - } - } + // First, get the list of physical partitions up to \\.\PHYSICALDRIVE15 + int maxDrives = 15; + List drives = []; + for (int drvIdx = 0; drvIdx <= maxDrives; drvIdx++) + drives.Add(@$"\\.\PHYSICALDRIVE{drvIdx}"); - // Then, open the file handle to these drives for us to be able to get the hard drive geometry - List driveHandles = []; - foreach (var (letter, partSize) in parts) + // Then, open the file handle to the physical drives for us to be able to filter them for partitions + List finalDrives = []; + foreach (string drive in drives) { - IntPtr driveHandle = PlatformWindowsInterop.CreateFile(letter, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.System, IntPtr.Zero); + IntPtr driveHandle = PlatformWindowsInterop.CreateFile(drive, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.System, IntPtr.Zero); if (driveHandle != new IntPtr(-1)) - driveHandles.Add(driveHandle); - } - - // Enumerate through the handles to try to get their drive geometry - for (int i = 0; i < driveHandles.Count; i++) - { - // Try to get drive storage info - IntPtr drive = driveHandles[i]; - bool result = PlatformWindowsInterop.DeviceIoControl(drive, PlatformWindowsInterop.EIOControlCode.DiskGetDriveGeometry, null, 0, out PlatformWindowsInterop.DISK_GEOMETRY drvGeom, Marshal.SizeOf(), out _, IntPtr.Zero); - if (!result) - continue; + { + maxDrives++; + finalDrives.Add(driveHandle); - // Get the hard disk size and number - bool partResult = PlatformWindowsInterop.DeviceIoControl(drive, PlatformWindowsInterop.EIOControlCode.StorageGetDeviceNumber, null, 0, out PlatformWindowsInterop.STORAGE_DEVICE_NUMBER devNumber, Marshal.SizeOf(), out _, IntPtr.Zero); - if (!partResult) - continue; - ulong hardDiskSize = (ulong)(drvGeom.Cylinders * drvGeom.TracksPerCylinder * drvGeom.SectorsPerTrack * drvGeom.BytesPerSector); - int diskNum = devNumber.DeviceNumber; + // Get the hard drive number + bool result = PlatformWindowsInterop.DeviceIoControl(driveHandle, PlatformWindowsInterop.EIOControlCode.DiskGetDriveGeometry, null, 0, out PlatformWindowsInterop.DISK_GEOMETRY drvGeom, Marshal.SizeOf(), out _, IntPtr.Zero); + if (!result) + continue; + bool numResult = PlatformWindowsInterop.DeviceIoControl(driveHandle, PlatformWindowsInterop.EIOControlCode.StorageGetDeviceNumber, null, 0, out PlatformWindowsInterop.STORAGE_DEVICE_NUMBER devNumber, Marshal.SizeOf(), out _, IntPtr.Zero); + if (!numResult) + continue; + ulong hardDiskSize = (ulong)(drvGeom.Cylinders * drvGeom.TracksPerCylinder * drvGeom.SectorsPerTrack * drvGeom.BytesPerSector); + int diskNum = devNumber.DeviceNumber; - // Install the initial hard disk part - if (diskParts.Count == 0 || !diskParts.Any((part) => part.HardDiskNumber == diskNum)) - { - partitions.Clear(); - diskParts.Add(new HardDiskPart + // Create a hard drive instance + var hdd = new HardDiskPart() { HardDiskSize = hardDiskSize, - }); - } + HardDiskNumber = diskNum, + }; - // Now, deal with making partition info classes - int partNum = devNumber.PartitionNumber; - partitions.Add(new HardDiskPart.PartitionPart - { - PartitionNumber = partNum, - PartitionSize = parts[i].partSize, - }); - diskParts[^1].HardDiskNumber = diskNum; - diskParts[^1].Partitions = [.. partitions]; + // Check the type + if (drvGeom.MediaType == PlatformWindowsInterop.MEDIA_TYPE.FixedMedia) + { + // Get the partitions + var parts = new List(); + IntPtr driveLayoutPtr = IntPtr.Zero; + int buffSize = 1024; + int error; + do + { + // Allocate the drive layout pointer + error = 0; + driveLayoutPtr = Marshal.AllocHGlobal(buffSize); + + // Try to get the drive layout + bool partResult = PlatformWindowsInterop.DeviceIoControl(driveHandle, PlatformWindowsInterop.EIOControlCode.DiskGetDriveLayoutEx, IntPtr.Zero, 0, driveLayoutPtr, (uint)buffSize, out _, IntPtr.Zero); + if (partResult) + { + // Get all the partitions + PlatformWindowsInterop.DRIVE_LAYOUT_INFORMATION_EX driveLayout = Marshal.PtrToStructure(driveLayoutPtr); + for (uint partIdx = 0; partIdx < driveLayout.PartitionCount; partIdx++) + { + // Make a pointer to a partition info instance + IntPtr ptr = new(driveLayoutPtr.ToInt64() + Marshal.OffsetOf(typeof(PlatformWindowsInterop.DRIVE_LAYOUT_INFORMATION_EX), "PartitionEntry").ToInt64() + (partIdx * Marshal.SizeOf(typeof(PlatformWindowsInterop.PARTITION_INFORMATION_EX)))); + PlatformWindowsInterop.PARTITION_INFORMATION_EX partInfo = Marshal.PtrToStructure(ptr); + + // Check to see if this partition is a recognizable MBR/GPT partition + if ((partInfo.PartitionStyle != PlatformWindowsInterop.PARTITION_STYLE.PARTITION_STYLE_MBR) || partInfo.Mbr.RecognizedPartition) + { + // Add this partition + parts.Add(new() + { + PartitionNumber = (int)partInfo.PartitionNumber, + PartitionSize = partInfo.PartitionLength, + }); + } + } + } + else + { + // Increase the buffer size by multiplying it by two. + error = Marshal.GetLastWin32Error(); + buffSize *= 2; + } + + // Free the drive layout handle + Marshal.FreeHGlobal(driveLayoutPtr); + driveLayoutPtr = IntPtr.Zero; + } while (error == 0x7A); + + // Add a hard disk + diskParts.Add(new() + { + HardDiskSize = hardDiskSize, + HardDiskNumber = diskNum, + Partitions = [.. parts], + }); + } + + // Close the handle + PlatformWindowsInterop.CloseHandle(driveHandle); + } } } catch (Exception ex) diff --git a/SpecProbe/Platform/PlatformWindowsInterop.cs b/SpecProbe/Platform/PlatformWindowsInterop.cs index caec8fc..abdf72b 100644 --- a/SpecProbe/Platform/PlatformWindowsInterop.cs +++ b/SpecProbe/Platform/PlatformWindowsInterop.cs @@ -420,6 +420,13 @@ public enum EIOControlCode : uint VideoSetDisplayBrightness = (EFileDevice.Video << 16) | (0x0127 << 2) | EMethod.Buffered | (0 << 14) } + public enum PARTITION_STYLE : uint + { + PARTITION_STYLE_MBR = 0, + PARTITION_STYLE_GPT = 1, + PARTITION_STYLE_RAW = 2 + } + [StructLayout(LayoutKind.Sequential)] internal struct DISK_GEOMETRY { @@ -431,13 +438,112 @@ internal struct DISK_GEOMETRY } [StructLayout(LayoutKind.Sequential)] - public struct STORAGE_DEVICE_NUMBER + internal struct STORAGE_DEVICE_NUMBER { public int DeviceType; public int DeviceNumber; public int PartitionNumber; } + [StructLayout(LayoutKind.Explicit)] + internal struct DRIVE_LAYOUT_INFORMATION_MBR + { + [FieldOffset(0)] + public uint Signature; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct DRIVE_LAYOUT_INFORMATION_GPT + { + [FieldOffset(0)] + public Guid DiskId; + [FieldOffset(16)] + public long StartingUsableOffset; + [FieldOffset(24)] + public long UsableLength; + [FieldOffset(32)] + public uint MaxPartitionCount; + } + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] + internal struct PARTITION_INFORMATION_GPT + { + [FieldOffset(0)] + public Guid PartitionType; + [FieldOffset(16)] + public Guid PartitionId; + [FieldOffset(32)] + public ulong Attributes; + [FieldOffset(40)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)] + public string Name; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct PARTITION_INFORMATION_MBR + { + #region Constants + public const byte PARTITION_ENTRY_UNUSED = 0x00; + public const byte PARTITION_FAT_12 = 0x01; + public const byte PARTITION_FAT_16 = 0x04; + public const byte PARTITION_EXTENDED = 0x05; + public const byte PARTITION_IFS = 0x07; + public const byte PARTITION_FAT32 = 0x0B; + public const byte PARTITION_LDM = 0x42; + public const byte PARTITION_NTFT = 0x80; + public const byte PARTITION_VALID_NTFT = 0xC0; + #endregion + + [FieldOffset(0)] + [MarshalAs(UnmanagedType.U1)] + public byte PartitionType; + [FieldOffset(1)] + [MarshalAs(UnmanagedType.I1)] + public bool BootIndicator; + [FieldOffset(2)] + [MarshalAs(UnmanagedType.I1)] + public bool RecognizedPartition; + [FieldOffset(4)] + public uint HiddenSectors; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct PARTITION_INFORMATION_EX + { + [FieldOffset(0)] + public PARTITION_STYLE PartitionStyle; + [FieldOffset(8)] + public long StartingOffset; + [FieldOffset(16)] + public long PartitionLength; + [FieldOffset(24)] + public uint PartitionNumber; + [FieldOffset(28)] + [MarshalAs(UnmanagedType.I1)] + public bool RewritePartition; + [FieldOffset(32)] + public PARTITION_INFORMATION_MBR Mbr; + [FieldOffset(32)] + public PARTITION_INFORMATION_GPT Gpt; + } + + /// + /// Contains extended information about a drive's partitions. + /// + [StructLayout(LayoutKind.Explicit)] + internal struct DRIVE_LAYOUT_INFORMATION_EX + { + [FieldOffset(0)] + public PARTITION_STYLE PartitionStyle; + [FieldOffset(4)] + public uint PartitionCount; + [FieldOffset(8)] + public DRIVE_LAYOUT_INFORMATION_MBR Mbr; + [FieldOffset(8)] + public DRIVE_LAYOUT_INFORMATION_GPT Gpt; + [FieldOffset(48)] + public PARTITION_INFORMATION_EX PartitionEntry; + } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] internal static extern IntPtr CreateFile( @@ -450,16 +556,20 @@ internal static extern IntPtr CreateFile( IntPtr templateFile ); - [DllImport("Kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll", SetLastError = true)] public static extern bool DeviceIoControl( IntPtr hDevice, - uint IoControlCode, - byte[] InBuffer, - int nInBufferSize, - byte[] OutBuffer, - int nOutBufferSize, - out int pBytesReturned, - IntPtr Overlapped + EIOControlCode dwIoControlCode, + IntPtr lpInBuffer, + uint nInBufferSize, + [Out] IntPtr lpOutBuffer, + uint nOutBufferSize, + out uint lpBytesReturned, + IntPtr lpOverlapped ); [DllImport("Kernel32.dll", SetLastError = true)]