/* * PROJECT: ReactOS Partition Information Tool * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Displays disk and partition information for MBR and GPT disks. * COPYRIGHT: Copyright 2001-2002 Eric Kohl * Copyright 2020-2021 Hermes Belusca-Maito */ /* INCLUDES *****************************************************************/ #include #include #define WIN32_NO_STATUS #include #include /* The maximum information a DISK_GEOMETRY_EX dynamic structure can contain */ typedef struct _DISK_GEOMETRY_EX_INTERNAL { DISK_GEOMETRY Geometry; LARGE_INTEGER DiskSize; DISK_PARTITION_INFO Partition; DISK_DETECTION_INFO Detection; } DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL; #define DRIVE_LAYOUT_INFO_ENTRY_SIZE \ RTL_FIELD_SIZE(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]) #define DRIVE_LAYOUT_INFO_SIZE(n) \ (FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) + \ ((n) * DRIVE_LAYOUT_INFO_ENTRY_SIZE)) #define DRIVE_LAYOUT_INFOEX_ENTRY_SIZE \ RTL_FIELD_SIZE(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) #define DRIVE_LAYOUT_INFOEX_SIZE(n) \ (FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) + \ ((n) * DRIVE_LAYOUT_INFOEX_ENTRY_SIZE)) /* Rounding up for number; not the same as ROUND_UP() with power-of-two sizes */ #define ROUND_UP_NUM(num, up) ((((num) + (up) - 1) / (up)) * (up)) // #define DUMP_DATA /* FORMATTING HELPERS *******************************************************/ static PCSTR PartitionStyleNames[] = {"MBR", "GPT", "RAW", "Unknown"}; #define PARTITION_STYLE_NAME(PartStyle) \ ( ((PartStyle) <= PARTITION_STYLE_RAW) \ ? PartitionStyleNames[(PartStyle)] \ : PartitionStyleNames[_countof(PartitionStyleNames)-1] ) static PCSTR DetectTypeNames[] = { "None", "DetectInt13", "DetectExInt13", "Unknown" }; #define DETECT_TYPE_NAME(DetectType) \ ( ((DetectType) <= DetectExInt13) \ ? DetectTypeNames[(DetectType)] \ : DetectTypeNames[_countof(DetectTypeNames)-1] ) #define GUID_FORMAT_STR "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" #define GUID_ELEMENTS(Guid) \ (Guid)->Data1, (Guid)->Data2, (Guid)->Data3, \ (Guid)->Data4[0], (Guid)->Data4[1], (Guid)->Data4[2], (Guid)->Data4[3], \ (Guid)->Data4[4], (Guid)->Data4[5], (Guid)->Data4[6], (Guid)->Data4[7] /* FUNCTIONS ****************************************************************/ #ifdef DUMP_DATA void HexDump( IN PVOID buffer, IN ULONG size) { ULONG_PTR offset = 0; PUCHAR ptr; while (offset < (size & ~15)) { ptr = (PUCHAR)((ULONG_PTR)buffer + offset); printf("%08lx %02hx %02hx %02hx %02hx %02hx %02hx %02hx %02hx-%02hx %02hx %02hx %02hx %02hx %02hx %02hx %02hx\n", offset, ptr[0], ptr[1], ptr[2] , ptr[3] , ptr[4] , ptr[5] , ptr[6] , ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); offset += 16; } if (offset < size) { ptr = (PUCHAR)((ULONG_PTR)buffer + offset); printf("%08lx ", offset); while (offset < size) { printf(" %02hx", *ptr); offset++; ptr++; } printf("\n"); } printf("\n"); } #endif void Usage(void) { puts("Usage: partinfo "); } int main(int argc, char *argv[]) { NTSTATUS Status; ULONG ulDrive; HANDLE hDisk; ULONG i; SYSTEM_DEVICE_INFORMATION DeviceInfo; IO_STATUS_BLOCK Iosb; ULONG BufferSize; union { DISK_GEOMETRY Info; DISK_GEOMETRY_EX_INTERNAL InfoEx; } DiskGeometry; PDISK_GEOMETRY pDiskGeometry; union { PARTITION_INFORMATION Info; PARTITION_INFORMATION_EX InfoEx; } PartInfo; PPARTITION_INFORMATION pPartInfo; PPARTITION_INFORMATION_EX pPartInfoEx; union { PDRIVE_LAYOUT_INFORMATION Info; PDRIVE_LAYOUT_INFORMATION_EX InfoEx; } LayoutBuffer; PVOID ptr; WCHAR DriveName[40]; if (argc != 2) { Usage(); return 0; } ulDrive = strtoul(argv[1], NULL, 10); if (errno != 0) { printf("Error: Malformed drive number\n"); return 0; } /* * Retrieve the number of disks on the system. */ Status = NtQuerySystemInformation(SystemDeviceInformation, &DeviceInfo, sizeof(DeviceInfo), &i); if (!NT_SUCCESS(Status)) { printf("NtQuerySystemInformation() failed (Status 0x%lx)\n", Status); return 0; } if (DeviceInfo.NumberOfDisks == 0) { printf("No disk drive installed!\n"); return 0; } if (ulDrive >= DeviceInfo.NumberOfDisks) { printf("Invalid disk drive number! Valid drive numbers: [0-%lu]\n", DeviceInfo.NumberOfDisks-1); return 0; } /* Build the full drive name */ swprintf(DriveName, L"\\\\.\\PHYSICALDRIVE%lu", ulDrive); /* Open the drive */ hDisk = CreateFileW(DriveName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDisk == INVALID_HANDLE_VALUE) { printf("Could not open drive!"); return 0; } printf("Drive number: %lu\n\n", ulDrive); /* * Get the drive geometry. */ Status = NtDeviceIoControlFile(hDisk, NULL, NULL, NULL, &Iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry.Info, sizeof(DiskGeometry.Info)); if (!NT_SUCCESS(Status)) { printf("NtDeviceIoControlFile(IOCTL_DISK_GET_DRIVE_GEOMETRY) failed! (Status 0x%lx)\n", Status); } else { pDiskGeometry = &DiskGeometry.Info; #ifdef DUMP_DATA HexDump(&DiskGeometry.Info, (ULONG)Iosb.Information); #endif printf("IOCTL_DISK_GET_DRIVE_GEOMETRY\n" "Cylinders: %I64u\nMediaType: 0x%x\nTracksPerCylinder: %lu\n" "SectorsPerTrack: %lu\nBytesPerSector: %lu\n", pDiskGeometry->Cylinders.QuadPart, pDiskGeometry->MediaType, pDiskGeometry->TracksPerCylinder, pDiskGeometry->SectorsPerTrack, pDiskGeometry->BytesPerSector); } /* * Get the extended drive geometry. */ printf("\n"); Status = NtDeviceIoControlFile(hDisk, NULL, NULL, NULL, &Iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &DiskGeometry.InfoEx, sizeof(DiskGeometry.InfoEx)); if (!NT_SUCCESS(Status)) { printf("NtDeviceIoControlFile(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX) failed! (Status 0x%lx)\n", Status); } else { // PDISK_DETECTION_INFO DiskDetectInfo = &DiskGeometry.InfoEx.Detection; // DiskGeometryGetDetect(&DiskGeometry.InfoEx); // PDISK_PARTITION_INFO DiskPartInfo = &DiskGeometry.InfoEx.Partition; // DiskGeometryGetPartition(&DiskGeometry.InfoEx); pDiskGeometry = &DiskGeometry.InfoEx.Geometry; #ifdef DUMP_DATA HexDump(&DiskGeometry.InfoEx, (ULONG)Iosb.Information); #endif printf("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX\n" "Cylinders: %I64u\nMediaType: 0x%x\nTracksPerCylinder: %lu\n" "SectorsPerTrack: %lu\nBytesPerSector: %lu\n" "DiskSize: %I64u\n", pDiskGeometry->Cylinders.QuadPart, pDiskGeometry->MediaType, pDiskGeometry->TracksPerCylinder, pDiskGeometry->SectorsPerTrack, pDiskGeometry->BytesPerSector, DiskGeometry.InfoEx.DiskSize.QuadPart); printf("SizeOfDetectInfo: %lu DetectionType: %u (%s)\n", DiskGeometry.InfoEx.Detection.SizeOfDetectInfo, DiskGeometry.InfoEx.Detection.DetectionType, DETECT_TYPE_NAME(DiskGeometry.InfoEx.Detection.DetectionType)); switch (DiskGeometry.InfoEx.Detection.DetectionType) { case DetectInt13: { PDISK_INT13_INFO pInt13 = &DiskGeometry.InfoEx.Detection.Int13; printf(" DriveSelect: %u MaxCylinders: %lu SectorsPerTrack: %u MaxHeads: %u NumberDrives: %u\n", pInt13->DriveSelect, pInt13->MaxCylinders, pInt13->SectorsPerTrack, pInt13->MaxHeads, pInt13->NumberDrives); break; } case DetectExInt13: { PDISK_EX_INT13_INFO pExInt13 = &DiskGeometry.InfoEx.Detection.ExInt13; printf(" ExBufferSize: %u ExFlags: %u\n" " ExCylinders: %lu ExHeads: %lu ExSectorsPerTrack: %lu\n" " ExSectorsPerDrive: %I64u ExSectorSize: %u ExReserved: %u\n", pExInt13->ExBufferSize, pExInt13->ExFlags, pExInt13->ExCylinders, pExInt13->ExHeads, pExInt13->ExSectorsPerTrack, pExInt13->ExSectorsPerDrive, pExInt13->ExSectorSize, pExInt13->ExReserved); break; } case DetectNone: default: break; } printf("SizeOfPartitionInfo: %lu PartitionStyle: %u [%s]\n", DiskGeometry.InfoEx.Partition.SizeOfPartitionInfo, DiskGeometry.InfoEx.Partition.PartitionStyle, PARTITION_STYLE_NAME(DiskGeometry.InfoEx.Partition.PartitionStyle)); if (DiskGeometry.InfoEx.Partition.PartitionStyle == PARTITION_STYLE_MBR) { printf(" Signature: 0x%08lx Checksum 0x%08lx\n", DiskGeometry.InfoEx.Partition.Mbr.Signature, DiskGeometry.InfoEx.Partition.Mbr.CheckSum); } else if (DiskGeometry.InfoEx.Partition.PartitionStyle == PARTITION_STYLE_GPT) { printf(" DiskId: {" GUID_FORMAT_STR "}\n", GUID_ELEMENTS(&DiskGeometry.InfoEx.Partition.Gpt.DiskId)); } else { /* Unknown */ printf("\n"); } } /* * Display partition 0 (i.e. the whole disk) information. */ printf("\n"); Status = NtDeviceIoControlFile(hDisk, NULL, NULL, NULL, &Iosb, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &PartInfo.Info, sizeof(PartInfo.Info)); if (!NT_SUCCESS(Status)) { printf("NtDeviceIoControlFile(IOCTL_DISK_GET_PARTITION_INFO) failed! (Status 0x%lx)\n", Status); } else { pPartInfo = &PartInfo.Info; #ifdef DUMP_DATA HexDump(&PartInfo.Info, (ULONG)Iosb.Information); #endif printf("IOCTL_DISK_GET_PARTITION_INFO\n" "nr: %ld boot: %1x type: 0x%x (%s) start: 0x%016I64x count: 0x%016I64x hidden: 0x%lx\n", pPartInfo->PartitionNumber, pPartInfo->BootIndicator, pPartInfo->PartitionType, pPartInfo->RecognizedPartition ? "Recognized" : "Not recognized", pPartInfo->StartingOffset.QuadPart, pPartInfo->PartitionLength.QuadPart, pPartInfo->HiddenSectors); } printf("\n"); Status = NtDeviceIoControlFile(hDisk, NULL, NULL, NULL, &Iosb, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &PartInfo.InfoEx, sizeof(PartInfo.InfoEx)); if (!NT_SUCCESS(Status)) { printf("NtDeviceIoControlFile(IOCTL_DISK_GET_PARTITION_INFO_EX) failed! (Status 0x%lx)\n", Status); } else { pPartInfoEx = &PartInfo.InfoEx; #ifdef DUMP_DATA HexDump(&PartInfo.InfoEx, (ULONG)Iosb.Information); #endif printf("IOCTL_DISK_GET_PARTITION_INFO_EX\n"); if (pPartInfoEx->PartitionStyle == PARTITION_STYLE_MBR) { printf("nr: %ld [%s] boot: %1x type: 0x%x (%s) start: 0x%016I64x count: 0x%016I64x hidden: 0x%lx\n", pPartInfoEx->PartitionNumber, PARTITION_STYLE_NAME(pPartInfoEx->PartitionStyle), pPartInfoEx->Mbr.BootIndicator, pPartInfoEx->Mbr.PartitionType, pPartInfoEx->Mbr.RecognizedPartition ? "Recognized" : "Not recognized", pPartInfoEx->StartingOffset.QuadPart, pPartInfoEx->PartitionLength.QuadPart, pPartInfoEx->Mbr.HiddenSectors); #if (NTDDI_VERSION >= NTDDI_WINBLUE) printf(" PartitionId: {" GUID_FORMAT_STR "}\n", GUID_ELEMENTS(&pPartInfoEx->Mbr.PartitionId)); #endif } else if (pPartInfoEx->PartitionStyle == PARTITION_STYLE_GPT) { printf("nr: %ld [%s]\n" " type : {" GUID_FORMAT_STR "}\n" " id : {" GUID_FORMAT_STR "}\n" " attrs: 0x%016I64x\n" " name : '%.*S'\n" " start: 0x%016I64x count: 0x%016I64x\n", pPartInfoEx->PartitionNumber, PARTITION_STYLE_NAME(pPartInfoEx->PartitionStyle), GUID_ELEMENTS(&pPartInfoEx->Gpt.PartitionType), GUID_ELEMENTS(&pPartInfoEx->Gpt.PartitionId), pPartInfoEx->Gpt.Attributes, (int)_countof(pPartInfoEx->Gpt.Name), pPartInfoEx->Gpt.Name, pPartInfoEx->StartingOffset.QuadPart, pPartInfoEx->PartitionLength.QuadPart); } } /* * Retrieve the legacy partition layout */ printf("\n"); /* Allocate a layout buffer with initially 4 partition entries (or 16 for NEC PC-98) */ BufferSize = DRIVE_LAYOUT_INFO_SIZE(IsNEC_98 ? 16 : 4); LayoutBuffer.Info = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, BufferSize); if (!LayoutBuffer.Info) { printf("Out of memory!"); goto Quit; } /* * Keep looping while the drive layout buffer is too small. * Iosb.Information or PartitionCount only contain actual info only * once NtDeviceIoControlFile(IOCTL_DISK_GET_DRIVE_LAYOUT) succeeds. */ for (;;) { Status = NtDeviceIoControlFile(hDisk, NULL, NULL, NULL, &Iosb, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, LayoutBuffer.Info, BufferSize); if (NT_SUCCESS(Status)) { /* We succeeded; compactify the layout structure but keep the * number of partition entries rounded up to a multiple of 4. */ #if 1 BufferSize = DRIVE_LAYOUT_INFO_SIZE(ROUND_UP_NUM(LayoutBuffer.Info->PartitionCount, 4)); #else BufferSize = (ULONG)Iosb.Information; #endif ptr = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, LayoutBuffer.Info, BufferSize); if (!ptr) { printf("Compactification failed; keeping original structure.\n"); } else { LayoutBuffer.Info = ptr; } Status = STATUS_SUCCESS; break; } if (Status != STATUS_BUFFER_TOO_SMALL) { printf("NtDeviceIoControlFile(IOCTL_DISK_GET_DRIVE_LAYOUT) failed! (Status 0x%lx)\n", Status); /* Bail out if any other error than "invalid function" has been emitted. * This happens for example when calling it on GPT disks. */ if (Status != STATUS_INVALID_DEVICE_REQUEST) { RtlFreeHeap(RtlGetProcessHeap(), 0, LayoutBuffer.Info); goto Quit; } else { /* Just stop getting this information */ break; } } /* Reallocate the buffer by chunks of 4 entries */ BufferSize += 4 * DRIVE_LAYOUT_INFO_ENTRY_SIZE; ptr = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, LayoutBuffer.Info, BufferSize); if (!ptr) { printf("Out of memory!"); RtlFreeHeap(RtlGetProcessHeap(), 0, LayoutBuffer.Info); goto Quit; } LayoutBuffer.Info = ptr; } if (Status == STATUS_SUCCESS) { #ifdef DUMP_DATA HexDump(LayoutBuffer.Info, (ULONG)Iosb.Information); #endif printf("IOCTL_DISK_GET_DRIVE_LAYOUT\n" "Partitions: %lu Signature: 0x%08lx\n", LayoutBuffer.Info->PartitionCount, LayoutBuffer.Info->Signature); for (i = 0; i < LayoutBuffer.Info->PartitionCount; i++) { pPartInfo = &LayoutBuffer.Info->PartitionEntry[i]; printf(" %ld: nr: %ld boot: %1x type: 0x%x (%s) start: 0x%016I64x count: 0x%016I64x hidden: 0x%lx\n", i, pPartInfo->PartitionNumber, pPartInfo->BootIndicator, pPartInfo->PartitionType, pPartInfo->RecognizedPartition ? "Recognized" : "Not recognized", pPartInfo->StartingOffset.QuadPart, pPartInfo->PartitionLength.QuadPart, pPartInfo->HiddenSectors); } } RtlFreeHeap(RtlGetProcessHeap(), 0, LayoutBuffer.Info); /* * Retrieve the extended partition layout */ printf("\n"); /* Allocate a layout buffer with initially 4 partition entries (or 16 for NEC PC-98) */ BufferSize = DRIVE_LAYOUT_INFOEX_SIZE(IsNEC_98 ? 16 : 4); LayoutBuffer.InfoEx = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, BufferSize); if (!LayoutBuffer.InfoEx) { printf("Out of memory!"); goto Quit; } /* * Keep looping while the drive layout buffer is too small. * Iosb.Information or PartitionCount only contain actual info only * once NtDeviceIoControlFile(IOCTL_DISK_GET_DRIVE_LAYOUT_EX) succeeds. */ for (;;) { Status = NtDeviceIoControlFile(hDisk, NULL, NULL, NULL, &Iosb, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, LayoutBuffer.InfoEx, BufferSize); if (NT_SUCCESS(Status)) { /* We succeeded; compactify the layout structure but keep the * number of partition entries rounded up to a multiple of 4. */ #if 1 BufferSize = DRIVE_LAYOUT_INFOEX_SIZE(ROUND_UP_NUM(LayoutBuffer.InfoEx->PartitionCount, 4)); #else BufferSize = (ULONG)Iosb.Information; #endif ptr = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, LayoutBuffer.InfoEx, BufferSize); if (!ptr) { printf("Compactification failed; keeping original structure.\n"); } else { LayoutBuffer.InfoEx = ptr; } Status = STATUS_SUCCESS; break; } if (Status != STATUS_BUFFER_TOO_SMALL) { printf("NtDeviceIoControlFile(IOCTL_DISK_GET_DRIVE_LAYOUT_EX) failed! (Status 0x%lx)\n", Status); /* Bail out if any other error than "invalid function" has been emitted */ if (Status != STATUS_INVALID_DEVICE_REQUEST) { RtlFreeHeap(RtlGetProcessHeap(), 0, LayoutBuffer.InfoEx); goto Quit; } else { /* Just stop getting this information */ break; } } /* Reallocate the buffer by chunks of 4 entries */ BufferSize += 4 * DRIVE_LAYOUT_INFOEX_ENTRY_SIZE; ptr = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, LayoutBuffer.InfoEx, BufferSize); if (!ptr) { printf("Out of memory!"); RtlFreeHeap(RtlGetProcessHeap(), 0, LayoutBuffer.InfoEx); goto Quit; } LayoutBuffer.InfoEx = ptr; } if (Status == STATUS_SUCCESS) { #ifdef DUMP_DATA HexDump(LayoutBuffer.InfoEx, (ULONG)Iosb.Information); #endif printf("IOCTL_DISK_GET_DRIVE_LAYOUT_EX\n" "PartitionStyle: %lu [%s]\n", LayoutBuffer.InfoEx->PartitionStyle, PARTITION_STYLE_NAME(LayoutBuffer.InfoEx->PartitionStyle)); if (LayoutBuffer.InfoEx->PartitionStyle == PARTITION_STYLE_MBR) { printf("Partitions: %lu Signature: 0x%08lx", LayoutBuffer.InfoEx->PartitionCount, LayoutBuffer.InfoEx->Mbr.Signature); #if (NTDDI_VERSION >= NTDDI_WIN10_RS1) printf(" Checksum 0x%08lx\n", LayoutBuffer.InfoEx->Mbr.CheckSum); #else printf("\n"); #endif for (i = 0; i < LayoutBuffer.InfoEx->PartitionCount; i++) { pPartInfoEx = &LayoutBuffer.InfoEx->PartitionEntry[i]; printf(" %ld: nr: %ld [%s] boot: %1x type: 0x%x (%s) start: 0x%016I64x count: 0x%016I64x hidden: 0x%lx\n", i, pPartInfoEx->PartitionNumber, PARTITION_STYLE_NAME(pPartInfoEx->PartitionStyle), pPartInfoEx->Mbr.BootIndicator, pPartInfoEx->Mbr.PartitionType, pPartInfoEx->Mbr.RecognizedPartition ? "Recognized" : "Not recognized", pPartInfoEx->StartingOffset.QuadPart, pPartInfoEx->PartitionLength.QuadPart, pPartInfoEx->Mbr.HiddenSectors); #if (NTDDI_VERSION >= NTDDI_WINBLUE) printf(" PartitionId: {" GUID_FORMAT_STR "}\n", GUID_ELEMENTS(&pPartInfoEx->Mbr.PartitionId)); #endif } } else if (LayoutBuffer.InfoEx->PartitionStyle == PARTITION_STYLE_GPT) { printf("Partitions: %lu MaxPartitionCount: %lu\n" "DiskId: {" GUID_FORMAT_STR "}\n" "StartingUsableOffset: 0x%016I64x UsableLength: 0x%016I64x\n", LayoutBuffer.InfoEx->PartitionCount, LayoutBuffer.InfoEx->Gpt.MaxPartitionCount, GUID_ELEMENTS(&LayoutBuffer.InfoEx->Gpt.DiskId), LayoutBuffer.InfoEx->Gpt.StartingUsableOffset.QuadPart, LayoutBuffer.InfoEx->Gpt.UsableLength.QuadPart); for (i = 0; i < LayoutBuffer.InfoEx->PartitionCount; i++) { pPartInfoEx = &LayoutBuffer.InfoEx->PartitionEntry[i]; printf(" %ld: nr: %ld [%s]\n" " type : {" GUID_FORMAT_STR "}\n" " id : {" GUID_FORMAT_STR "}\n" " attrs: 0x%016I64x\n" " name : '%.*S'\n" " start: 0x%016I64x count: 0x%016I64x\n", i, pPartInfoEx->PartitionNumber, PARTITION_STYLE_NAME(pPartInfoEx->PartitionStyle), GUID_ELEMENTS(&pPartInfoEx->Gpt.PartitionType), GUID_ELEMENTS(&pPartInfoEx->Gpt.PartitionId), pPartInfoEx->Gpt.Attributes, (int)_countof(pPartInfoEx->Gpt.Name), pPartInfoEx->Gpt.Name, pPartInfoEx->StartingOffset.QuadPart, pPartInfoEx->PartitionLength.QuadPart); } } } RtlFreeHeap(RtlGetProcessHeap(), 0, LayoutBuffer.InfoEx); Quit: CloseHandle(hDisk); return 0; }