├── LICENSE ├── ListDisk.sln ├── ListDisk ├── ListDisk.cpp ├── ListDisk.h ├── ListDisk.ico ├── ListDisk.rc ├── ListDisk.vcxproj ├── ListDisk.vcxproj.filters └── ListDisk.vcxproj.user └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 George King 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ListDisk.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.34301.259 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ListDisk", "ListDisk\ListDisk.vcxproj", "{E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release XP SSE|x64 = Release XP SSE|x64 13 | Release XP SSE|x86 = Release XP SSE|x86 14 | Release XP|x64 = Release XP|x64 15 | Release XP|x86 = Release XP|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Debug|x64.ActiveCfg = Debug|x64 21 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Debug|x64.Build.0 = Debug|x64 22 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Debug|x86.ActiveCfg = Debug|Win32 23 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Debug|x86.Build.0 = Debug|Win32 24 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP SSE|x64.ActiveCfg = Release XP SSE|x64 25 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP SSE|x64.Build.0 = Release XP SSE|x64 26 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP SSE|x86.ActiveCfg = Release XP SSE|Win32 27 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP SSE|x86.Build.0 = Release XP SSE|Win32 28 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP|x64.ActiveCfg = Release XP|x64 29 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP|x64.Build.0 = Release XP|x64 30 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP|x86.ActiveCfg = Release XP|Win32 31 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release XP|x86.Build.0 = Release XP|Win32 32 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release|x64.ActiveCfg = Release|x64 33 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release|x64.Build.0 = Release|x64 34 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release|x86.ActiveCfg = Release|Win32 35 | {E90BD5B9-2D7B-4DAE-AA74-A7FA3C177258}.Release|x86.Build.0 = Release|Win32 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {3EE050B2-C3DB-4AEA-A4EC-147B110C53CB} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /ListDisk/ListDisk.cpp: -------------------------------------------------------------------------------- 1 | #define _WIN32_IE 0x0600 2 | #define NTDDI_VERSION NTDDI_WINXP 3 | #define _WIN32_WINNT 0x0501 4 | #define WINVER 0x0501 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "ListDisk.h" 28 | 29 | 30 | // Define GPT partition type GUIDs 31 | EXTERN_C const GUID PARTITION_MSFT_RESERVED_GUID = { 0xE3C9E316, 0x0B5C, 0x4DB8, { 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE } }; 32 | EXTERN_C const GUID PARTITION_BASIC_DATA_GUID = { 0xEBD0A0A2, 0xB9E5, 0x4433, { 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 } }; 33 | EXTERN_C const GUID PARTITION_LDM_DATA_GUID = { 0xA0A2B9E5, 0x4333, 0x87C0, { 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7, 0x00, 0x00 } }; 34 | EXTERN_C const GUID PARTITION_WINDOWS_RE_GUID = { 0xde94bba4, 0x06d1, 0x4d40, { 0xa1, 0x6a, 0xbf, 0xd5, 0x01, 0x79, 0xd6, 0xac } }; 35 | EXTERN_C const GUID PARTITION_BIOS_BOOT_GUID = { 0x21686148, 0x6449, 0x6E6F, { 0x74, 0x6F, 0x44, 0x43, 0x00, 0x00, 0x00, 0x00 } }; 36 | 37 | // Define additional GPT partition type GUIDs 38 | EXTERN_C const GUID PARTITION_LINUX_DATA_GUID = { 0x0FC63DAF, 0x8483, 0x4772, { 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0xE4, 0x5A } }; 39 | EXTERN_C const GUID PARTITION_BDE_GUID = { 0x5808C8AA, 0x7E8F, 0x42E0, { 0x85, 0xD2, 0xE1, 0xE9, 0x04, 0x34, 0xCF, 0xB3 } }; 40 | EXTERN_C const GUID PARTITION_ESP_GUID = { 0xC12A7328, 0xF81F, 0x11D2, { 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B } }; 41 | 42 | DEFINE_GUID(PARTITION_STORAGE_SPACES_GUID, 0xE75CAF8F, 0xF680, 0x4CEE, 0xAF, 0xA3, 0xB0, 0x01, 0xE5, 0x6E, 0xFC, 0x2E); 43 | DEFINE_GUID(PARTITION_STORAGE_SPACES_RE_GUID, 0xA19D880F, 0x05FC, 0x4D3B, 0xA0, 0x06, 0x74, 0x3F, 0x37, 0x23, 0x06, 0xFE); 44 | 45 | // Define MBR partition type constants 46 | #define PARTITION_FAT12 0x01 47 | #define PARTITION_HIDDEN_FAT12 0x11 // Hidden FAT12 48 | #define PARTITION_FAT16_SMALL 0x04 49 | #define PARTITION_HIDDEN_FAT16_SMALL 0x14 // Hidden FAT16 <32M 50 | #define PARTITION_EXTENDED_CHS 0x05 51 | #define PARTITION_FAT16 0x06 52 | #define PARTITION_HIDDEN_FAT16 0x16 // Hidden FAT16 53 | #define PARTITION_NTFS 0x07 // This ID is also commonly used for exFAT partitions 54 | #define PARTITION_NTFS_HIDDEN 0x17 // Hidden NTFS or Hidden HPFS 55 | #define PARTITION_FAT32_CHS 0x0B 56 | #define PARTITION_HIDDEN_FAT32_CHS 0x1B // Hidden FAT32 CHS 57 | #define PARTITION_FAT32_LBA 0x0C 58 | #define PARTITION_HIDDEN_FAT32_LBA 0x1C // Hidden FAT32 LBA 59 | #define PARTITION_FAT16_LBA 0x0E 60 | #define PARTITION_HIDDEN_FAT16_LBA 0x1E // Hidden FAT16 LBA 61 | #define PARTITION_EXTENDED_LBA 0x0F 62 | #define PARTITION_OS2_BOOT_MANAGER 0x0A // OS/2 Boot Manager/Extended Partition 63 | #define PARTITION_WINDOWS_RE 0x27 // Windows Recovery Environment 64 | 65 | // Additional MBR partition types 66 | #define PARTITION_LINUX 0x83 67 | #define PARTITION_LINUX_SWAP 0x82 68 | #define PARTITION_LINUX_LVM 0x8E 69 | #define PARTITION_EFI_SYSTEM 0xEF // EFI File System 70 | #define PARTITION_MSFT_RESERVED 0x42 71 | #define PARTITION_LINUX_EXTENDED 0x85 72 | #define PARTITION_NTFS_VOLUME_SET 0x86 73 | #define PARTITION_NTFS_VOLUME_SET_2 0x87 74 | #define PARTITION_LINUX_PLAINTEXT 0x91 75 | #define PARTITION_HIDDEN_LINUX 0x93 // Hidden Linux Native 76 | #define PARTITION_FREEBSD_SLICE 0xA5 77 | #define PARTITION_OPENBSD_SLICE 0xA6 78 | #define PARTITION_NETBSD_SLICE 0xA9 79 | #define PARTITION_NEXTSTEP 0xA7 80 | #define PARTITION_MAC_OS_X 0xAB 81 | #define PARTITION_GPT_PROTECTIVE 0xEE // GPT Protective 82 | 83 | // Additional specific types (optional depending on use case) 84 | #define PARTITION_SOLARIS 0xBE 85 | #define PARTITION_MAC_HFS 0xAF 86 | #define PARTITION_AMIGA 0xDB 87 | #define PARTITION_BSDI 0xEB 88 | 89 | #define STATUS_SUCCESS (0x00000000) 90 | 91 | // Define missing bus types if they are not defined by the SDK 92 | #ifndef BusTypeiScsi 93 | #define BusTypeiScsi 9 94 | #endif 95 | 96 | #ifndef BusTypeSas 97 | #define BusTypeSas 10 98 | #endif 99 | 100 | #ifndef BusTypeSata 101 | #define BusTypeSata 11 102 | #endif 103 | 104 | #ifndef BusTypeSd 105 | #define BusTypeSd 12 106 | #endif 107 | 108 | #ifndef BusTypeMmc 109 | #define BusTypeMmc 13 110 | #endif 111 | 112 | #ifndef BusTypeVirtual 113 | #define BusTypeVirtual 14 114 | #endif 115 | 116 | #ifndef BusTypeFileBackedVirtual 117 | #define BusTypeFileBackedVirtual 15 118 | #endif 119 | 120 | #ifndef BusTypeSpaces 121 | #define BusTypeSpaces 16 122 | #endif 123 | 124 | #ifndef BusTypeNvme 125 | #define BusTypeNvme 17 126 | #endif 127 | 128 | #ifndef BusTypeSCM 129 | #define BusTypeSCM 18 130 | #endif 131 | 132 | #ifndef BusTypeUfs 133 | #define BusTypeUfs 19 134 | #endif 135 | 136 | typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); 137 | 138 | enum class Align { 139 | Left, 140 | Right 141 | }; 142 | 143 | struct PartitionInfo { 144 | DWORD DiskNumber = 0; // Physical disk number 145 | DWORD PartitionNumber = 0; // Physical partition number 146 | PARTITION_STYLE PartitionStyle = PARTITION_STYLE_RAW; 147 | LONGLONG PartitionLength = 0; // Size 148 | LONGLONG PartitionOffset = 0; // Offset of the partition from the start of the disk 149 | std::wstring VolumeName; // Label 150 | std::wstring FileSystem; 151 | std::wstring DriveLetter; 152 | std::wstring MountPoint; 153 | std::wstring MountPointName; /* Volume mount point like \ ? \Volume{24379f2b-7a05-44b4-a9c3-6ef5fd068451} */ 154 | std::wstring PartitionType; 155 | GUID PartitionId = {}; // Added for GPT partition information 156 | std::wstring GptName; // For GPT partition name - doesn't contain all defined names 157 | BOOLEAN BootIndicator = FALSE; // MBR Boot indicator 158 | // New fields 159 | std::wstring Vendor; 160 | std::wstring Product; 161 | std::wstring SerialNumber; 162 | std::wstring BusType; 163 | BOOLEAN Removable = FALSE; 164 | BOOLEAN TrimSupported = FALSE; 165 | std::wstring HBTL; // Host Bus Target LUN 166 | 167 | // New fields for partition attributes 168 | ULONGLONG GptAttributes = 0; // GPT-specific attributes 169 | BYTE MbrPartitionTypeCode = 0; // New field to store the raw MBR partition type code 170 | 171 | // Additional fields for more comprehensive information 172 | DWORD PhysicalSectorSize = 0; // Physical sector size of the disk 173 | DWORD LogicalSectorSize = 0; // Logical sector size of the partition 174 | std::wstring DiskModel; // Disk model number 175 | std::wstring PartitionHealthStatus; // Health status of the partition 176 | BOOLEAN CompressionEnabled = FALSE; // Is compression enabled? 177 | std::wstring EncryptionStatus; // Status of encryption on the partition 178 | BOOLEAN ReadOnly = FALSE; // Is the partition read-only? 179 | BOOLEAN SystemPartition = FALSE; // Is it a system or boot partition? 180 | std::wstring RAIDLevel; // RAID level if applicable 181 | std::wstring FilesystemVersion; // Version of the filesystem 182 | std::wstring FilesystemFlags; // Filesystem-specific flags 183 | }; 184 | 185 | 186 | // Helper function to convert ANSI string to wstring 187 | std::wstring AnsiToWString(const char* ansiStr) { 188 | if (!ansiStr) return L""; 189 | int len = MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, nullptr, 0); 190 | if (len == 0) return L""; 191 | std::wstring wstr(len, L'\0'); 192 | MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, &wstr[0], len); 193 | return wstr; 194 | } 195 | 196 | 197 | // Helper function to map BusType to string 198 | std::wstring GetBusTypeString(STORAGE_BUS_TYPE busType) { 199 | switch (busType) { 200 | case BusTypeScsi: return L"SCSI"; 201 | case BusTypeAtapi: return L"ATAPI"; 202 | case BusTypeAta: return L"ATA"; 203 | case BusType1394: return L"1394"; 204 | case BusTypeSsa: return L"SSA"; 205 | case BusTypeFibre: return L"Fibre"; 206 | case BusTypeUsb: return L"USB"; 207 | case BusTypeRAID: return L"RAID"; 208 | case BusTypeiScsi: return L"iSCSI"; 209 | case BusTypeSas: return L"SAS"; 210 | case BusTypeSata: return L"SATA"; 211 | case BusTypeSd: return L"SD"; 212 | case BusTypeMmc: return L"MMC"; 213 | case BusTypeVirtual: return L"Virtual"; 214 | case BusTypeFileBackedVirtual: return L"FileBackedVirtual"; 215 | case BusTypeSpaces: return L"Spaces"; 216 | case BusTypeNvme: return L"NVMe"; 217 | case BusTypeSCM: return L"SCM"; 218 | case BusTypeUfs: return L"UFS"; 219 | default: return L"Unknown"; 220 | } 221 | } 222 | 223 | 224 | // Function to get partition information 225 | std::vector GetPartitionInfo() { 226 | std::vector partitions; 227 | HANDLE hDrive = INVALID_HANDLE_VALUE; 228 | HANDLE hVolume = INVALID_HANDLE_VALUE; 229 | DWORD bytesReturned = 0; 230 | 231 | int driveIndex = 0; 232 | while (true) { 233 | WCHAR driveName[25]; 234 | swprintf_s(driveName, L"\\\\.\\PhysicalDrive%d", driveIndex); 235 | 236 | hDrive = CreateFileW(driveName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 237 | 238 | if (hDrive == INVALID_HANDLE_VALUE) { 239 | if (GetLastError() == ERROR_FILE_NOT_FOUND) { 240 | break; // No more drives to check 241 | } 242 | std::cerr << "Failed to open " << driveName << ", Error: " << GetLastError() << std::endl; 243 | driveIndex++; 244 | continue; 245 | } 246 | 247 | DWORD bufferSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 128; 248 | std::unique_ptr buffer(new char[bufferSize]); 249 | DRIVE_LAYOUT_INFORMATION_EX* driveLayout = reinterpret_cast(buffer.get()); 250 | 251 | BOOL result = DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, driveLayout, bufferSize, &bytesReturned, NULL); 252 | if (result) { 253 | for (DWORD p = 0; p < driveLayout->PartitionCount; ++p) { 254 | PARTITION_INFORMATION_EX& partition = driveLayout->PartitionEntry[p]; 255 | if (partition.PartitionNumber != 0) { 256 | PartitionInfo info; 257 | info.DiskNumber = driveIndex; 258 | info.PartitionNumber = partition.PartitionNumber; 259 | info.PartitionStyle = partition.PartitionStyle; 260 | info.PartitionLength = partition.PartitionLength.QuadPart; 261 | info.PartitionOffset = partition.StartingOffset.QuadPart; 262 | 263 | if (partition.PartitionStyle == PARTITION_STYLE_GPT) { 264 | PARTITION_INFORMATION_GPT& gptPartition = partition.Gpt; 265 | info.PartitionId = gptPartition.PartitionId; 266 | info.GptAttributes = gptPartition.Attributes; // Store GPT attributes 267 | 268 | wchar_t gptNameArray[36] = {}; 269 | wcscpy_s(gptNameArray, gptPartition.Name); 270 | info.GptName = std::wstring(gptNameArray); 271 | 272 | if (IsEqualGUID(gptPartition.PartitionType, PARTITION_ESP_GUID)) 273 | info.PartitionType = L"EFI System"; 274 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_MSFT_RESERVED_GUID)) 275 | info.PartitionType = L"Reserved"; 276 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_BASIC_DATA_GUID)) 277 | info.PartitionType = L"Basic Data"; 278 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_LDM_DATA_GUID)) 279 | info.PartitionType = L"LDM Data"; 280 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_WINDOWS_RE_GUID)) 281 | info.PartitionType = L"Windows RE"; 282 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_BIOS_BOOT_GUID)) 283 | info.PartitionType = L"BIOS Boot"; 284 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_LINUX_DATA_GUID)) 285 | info.PartitionType = L"Linux Data"; 286 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_BDE_GUID)) 287 | info.PartitionType = L"BitLocker"; 288 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_STORAGE_SPACES_GUID)) 289 | info.PartitionType = L"Storage Spaces"; 290 | else if (IsEqualGUID(gptPartition.PartitionType, PARTITION_STORAGE_SPACES_RE_GUID)) 291 | info.PartitionType = L"Storage Spaces Recovery"; 292 | else 293 | info.PartitionType = L"Unknown"; 294 | } 295 | else if (partition.PartitionStyle == PARTITION_STYLE_MBR) { 296 | info.MbrPartitionTypeCode = partition.Mbr.PartitionType; // Store raw MBR partition type code 297 | 298 | switch (partition.Mbr.PartitionType) { 299 | case PARTITION_FAT12: info.PartitionType = L"FAT12"; break; 300 | case PARTITION_HIDDEN_FAT12: info.PartitionType = L"Hidden FAT12"; break; 301 | case PARTITION_FAT16_SMALL: info.PartitionType = L"FAT16 (small)"; break; 302 | case PARTITION_HIDDEN_FAT16_SMALL: info.PartitionType = L"Hidden FAT16 (small)"; break; 303 | case PARTITION_EXTENDED_CHS: info.PartitionType = L"Extended (CHS)"; break; 304 | case PARTITION_FAT16: info.PartitionType = L"FAT16"; break; 305 | case PARTITION_HIDDEN_FAT16: info.PartitionType = L"Hidden FAT16"; break; 306 | case PARTITION_NTFS: info.PartitionType = L"NTFS"; break; 307 | case PARTITION_NTFS_HIDDEN: info.PartitionType = L"NTFS Hidden"; break; 308 | case PARTITION_FAT32_CHS: info.PartitionType = L"FAT32 (CHS)"; break; 309 | case PARTITION_HIDDEN_FAT32_CHS: info.PartitionType = L"Hidden FAT32 (CHS)"; break; 310 | case PARTITION_FAT32_LBA: info.PartitionType = L"FAT32 (LBA)"; break; 311 | case PARTITION_HIDDEN_FAT32_LBA: info.PartitionType = L"Hidden FAT32 (LBA)"; break; 312 | case PARTITION_FAT16_LBA: info.PartitionType = L"FAT16 (LBA)"; break; 313 | case PARTITION_HIDDEN_FAT16_LBA: info.PartitionType = L"Hidden FAT16 (LBA)"; break; 314 | case PARTITION_EXTENDED_LBA: info.PartitionType = L"Extended (LBA)"; break; 315 | case PARTITION_LINUX: info.PartitionType = L"Linux"; break; 316 | case PARTITION_LINUX_SWAP: info.PartitionType = L"Linux Swap"; break; 317 | case PARTITION_LINUX_LVM: info.PartitionType = L"Linux LVM"; break; 318 | case PARTITION_EFI_SYSTEM: info.PartitionType = L"EFI System"; break; 319 | case PARTITION_MSFT_RESERVED: info.PartitionType = L"MSFT Reserved"; break; 320 | case PARTITION_LINUX_EXTENDED: info.PartitionType = L"Linux Extended"; break; 321 | case PARTITION_NTFS_VOLUME_SET: info.PartitionType = L"NTFS Volume Set"; break; 322 | case PARTITION_NTFS_VOLUME_SET_2: info.PartitionType = L"NTFS Volume Set 2"; break; 323 | case PARTITION_LINUX_PLAINTEXT: info.PartitionType = L"Linux Plaintext"; break; 324 | case PARTITION_HIDDEN_LINUX: info.PartitionType = L"Hidden Linux Native"; break; 325 | case PARTITION_FREEBSD_SLICE: info.PartitionType = L"FreeBSD Slice"; break; 326 | case PARTITION_OPENBSD_SLICE: info.PartitionType = L"OpenBSD Slice"; break; 327 | case PARTITION_NETBSD_SLICE: info.PartitionType = L"NetBSD Slice"; break; 328 | case PARTITION_NEXTSTEP: info.PartitionType = L"NeXTSTEP"; break; 329 | case PARTITION_MAC_OS_X: info.PartitionType = L"Mac OS X"; break; 330 | case PARTITION_GPT_PROTECTIVE: info.PartitionType = L"GPT Protective"; break; 331 | case PARTITION_SOLARIS: info.PartitionType = L"Solaris"; break; 332 | case PARTITION_MAC_HFS: info.PartitionType = L"Mac HFS"; break; 333 | case PARTITION_AMIGA: info.PartitionType = L"Amiga"; break; 334 | case PARTITION_BSDI: info.PartitionType = L"BSDI"; break; 335 | default: info.PartitionType = L"Unknown"; break; 336 | } 337 | 338 | if (partition.Mbr.BootIndicator != 0) { 339 | info.PartitionType += L" *"; 340 | info.BootIndicator = partition.Mbr.BootIndicator; 341 | } 342 | } 343 | 344 | // Generate the volume path 345 | WCHAR volumePath[MAX_PATH]; 346 | swprintf_s(volumePath, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%d\\Partition%d", info.DiskNumber, info.PartitionNumber); 347 | 348 | // Get volume information 349 | WCHAR volumeName[MAX_PATH] = { 0 }; 350 | WCHAR fileSystem[MAX_PATH] = { 0 }; 351 | 352 | hVolume = CreateFileW(volumePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 353 | 354 | if (hVolume != INVALID_HANDLE_VALUE) { 355 | WCHAR rootPath[MAX_PATH] = { 0 }; 356 | if (GetVolumePathNameW(volumePath, rootPath, MAX_PATH)) { 357 | info.MountPoint = rootPath; 358 | } 359 | 360 | if (GetVolumeInformationW(rootPath, volumeName, MAX_PATH, NULL, NULL, NULL, fileSystem, MAX_PATH)) { 361 | info.VolumeName = std::wstring(volumeName); 362 | info.FileSystem = std::wstring(fileSystem); 363 | } 364 | 365 | WCHAR volumeName[MAX_PATH] = { 0 }; 366 | if (GetVolumeNameForVolumeMountPointW(rootPath, volumeName, MAX_PATH)) { 367 | info.MountPointName = volumeName; 368 | 369 | // Get the drive letter associated with this volume. 370 | std::vector buffer(MAX_PATH); 371 | DWORD bufferSize = static_cast(buffer.size()); 372 | 373 | if (GetVolumePathNamesForVolumeNameW(volumeName, buffer.data(), bufferSize, &bufferSize)) { 374 | info.DriveLetter = buffer.data(); 375 | } 376 | } 377 | 378 | // Fallback: Check for the drive letter using DefineDosDevice symbolic links 379 | if (info.DriveLetter.empty()) { 380 | WCHAR dosDeviceList[MAX_PATH * 100] = { 0 }; 381 | DWORD charCount = QueryDosDeviceW(NULL, dosDeviceList, ARRAYSIZE(dosDeviceList)); 382 | if (charCount != 0) { 383 | for (WCHAR* dev = dosDeviceList; *dev; dev += wcslen(dev) + 1) { 384 | WCHAR devicePath[MAX_PATH] = { 0 }; 385 | if (QueryDosDeviceW(dev, devicePath, ARRAYSIZE(devicePath)) != 0) { 386 | if (wcsstr(devicePath, volumePath + 14) != NULL) { 387 | info.DriveLetter = dev; 388 | break; 389 | } 390 | } 391 | } 392 | } 393 | } 394 | 395 | CloseHandle(hVolume); 396 | } 397 | 398 | // Extended information retrieval 399 | STORAGE_PROPERTY_QUERY storageQuery = {}; 400 | storageQuery.PropertyId = StorageDeviceProperty; 401 | storageQuery.QueryType = PropertyStandardQuery; 402 | 403 | STORAGE_DESCRIPTOR_HEADER storageHeader = {}; 404 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &storageQuery, sizeof(storageQuery), &storageHeader, sizeof(storageHeader), &bytesReturned, NULL)) { 405 | std::unique_ptr descriptorBuffer(new BYTE[storageHeader.Size]); 406 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &storageQuery, sizeof(storageQuery), descriptorBuffer.get(), storageHeader.Size, &bytesReturned, NULL)) { 407 | auto deviceDescriptor = reinterpret_cast(descriptorBuffer.get()); 408 | 409 | if (deviceDescriptor->VendorIdOffset) { 410 | info.Vendor = AnsiToWString(reinterpret_cast(descriptorBuffer.get() + deviceDescriptor->VendorIdOffset)); 411 | } 412 | if (deviceDescriptor->ProductIdOffset) { 413 | info.Product = AnsiToWString(reinterpret_cast(descriptorBuffer.get() + deviceDescriptor->ProductIdOffset)); 414 | } 415 | if (deviceDescriptor->SerialNumberOffset) { 416 | info.SerialNumber = AnsiToWString(reinterpret_cast(descriptorBuffer.get() + deviceDescriptor->SerialNumberOffset)); 417 | } 418 | 419 | // Get Bus Type 420 | info.BusType = GetBusTypeString(deviceDescriptor->BusType); 421 | 422 | // Check if the media is removable 423 | info.Removable = (deviceDescriptor->RemovableMedia != 0); 424 | } 425 | } 426 | 427 | // Query for Trim support 428 | STORAGE_PROPERTY_QUERY trimQuery = { StorageDeviceTrimProperty, PropertyStandardQuery }; 429 | DEVICE_TRIM_DESCRIPTOR trimDescriptor = {}; 430 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &trimQuery, sizeof(trimQuery), &trimDescriptor, sizeof(trimDescriptor), &bytesReturned, NULL)) { 431 | info.TrimSupported = (trimDescriptor.TrimEnabled != 0); 432 | } 433 | 434 | // Query for SCSI address 435 | SCSI_ADDRESS scsiAddress = {}; 436 | if (DeviceIoControl(hDrive, IOCTL_SCSI_GET_ADDRESS, NULL, 0, &scsiAddress, sizeof(scsiAddress), &bytesReturned, NULL)) { 437 | std::wstringstream ss; 438 | ss << scsiAddress.PortNumber << L":" << scsiAddress.PathId << L":" << scsiAddress.TargetId << L":" << scsiAddress.Lun; 439 | info.HBTL = ss.str(); 440 | } 441 | 442 | // Additional fields (e.g., physical and logical sector size) 443 | DISK_GEOMETRY_EX diskGeometry = {}; 444 | if (DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &diskGeometry, sizeof(diskGeometry), &bytesReturned, NULL)) { 445 | info.PhysicalSectorSize = diskGeometry.Geometry.BytesPerSector; 446 | info.LogicalSectorSize = diskGeometry.Geometry.BytesPerSector; // Assuming same value for now 447 | } 448 | 449 | // Example: Checking if the partition is read-only 450 | if (!DeviceIoControl(hVolume, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, &bytesReturned, NULL)) { 451 | info.ReadOnly = true; 452 | } 453 | 454 | partitions.push_back(info); 455 | } 456 | } 457 | } 458 | else { 459 | std::cerr << "IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed for " << driveName << " with error " << GetLastError() << std::endl; 460 | } 461 | 462 | CloseHandle(hDrive); 463 | driveIndex++; 464 | } 465 | 466 | return partitions; 467 | } 468 | 469 | 470 | std::wstring PartitionStyleToWString(PARTITION_STYLE style) { 471 | switch (style) { 472 | case PARTITION_STYLE_MBR: return L"MBR"; 473 | case PARTITION_STYLE_GPT: return L"GPT"; 474 | case PARTITION_STYLE_RAW: return L"RAW"; 475 | default: return L"Unknown"; 476 | } 477 | } 478 | 479 | 480 | std::wstring FormatSize(LONGLONG size) { 481 | std::wstringstream stream; 482 | stream << std::right << std::setw(7); 483 | if (size >= (1LL << 40)) { 484 | stream << std::fixed << std::setprecision(2) << (size / (double)(1LL << 40)) << L" TB"; 485 | } 486 | else if (size >= (1LL << 30)) { 487 | stream << std::fixed << std::setprecision(2) << (size / (double)(1LL << 30)) << L" GB"; 488 | } 489 | else if (size >= (1LL << 20)) { 490 | stream << std::fixed << std::setprecision(2) << (size / (double)(1LL << 20)) << L" MB"; 491 | } 492 | else if (size >= (1LL << 10)) { 493 | stream << std::fixed << std::setprecision(2) << (size / (double)(1LL << 10)) << L" KB"; 494 | } 495 | else { 496 | stream << size << L" Bytes"; 497 | } 498 | return stream.str(); 499 | } 500 | 501 | 502 | std::wstring FormatLetter(const std::wstring& input) { 503 | std::wstring formatted = input; 504 | 505 | // Check if the last character is a backslash 506 | if (!formatted.empty() && formatted.back() == L'\\') { 507 | // Remove the last character (backslash) 508 | formatted.pop_back(); 509 | } 510 | 511 | return formatted; 512 | } 513 | 514 | 515 | bool IsDirectoryEmpty(const wchar_t* path) { 516 | WIN32_FIND_DATAW findFileData; 517 | HANDLE findHandle = FindFirstFileW((std::wstring(path) + L"\\*").c_str(), &findFileData); 518 | 519 | if (findHandle == INVALID_HANDLE_VALUE) { 520 | return true; // Directory is empty or doesn't exist 521 | } 522 | else { 523 | FindClose(findHandle); 524 | return false; // Directory contains files or subdirectories 525 | } 526 | } 527 | 528 | 529 | std::wstring GetMountPointName(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber) { 530 | for (const auto& partition : partitions) { 531 | if (partition.DiskNumber == diskNumber && partition.PartitionNumber == partitionNumber) { 532 | return partition.MountPointName; 533 | } 534 | } 535 | return L""; // Return empty string if not found 536 | } 537 | 538 | 539 | std::wstring GetDriveLetter(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber) { 540 | for (const auto& partition : partitions) { 541 | if (partition.DiskNumber == diskNumber && partition.PartitionNumber == partitionNumber) { 542 | // std::wcout << L"Found Drive Letter: " << partition.DriveLetter << std::endl; // Debug print 543 | if (!partition.DriveLetter.empty()) { 544 | return partition.DriveLetter; 545 | } 546 | } 547 | } 548 | //std::wcerr << L"Drive letter not found for Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 549 | return L""; // Return empty string if not found 550 | } 551 | 552 | 553 | PARTITION_STYLE GetPartitionStyle(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber) { 554 | for (const auto& partition : partitions) { 555 | if (partition.DiskNumber == diskNumber && partition.PartitionNumber == partitionNumber) { 556 | return partition.PartitionStyle; 557 | } 558 | } 559 | return PARTITION_STYLE_RAW; 560 | // If not found, throw an exception 561 | //throw std::runtime_error("Partition not found for Disk " + std::to_string(diskNumber) + ", Partition " + std::to_string(partitionNumber)); 562 | } 563 | 564 | 565 | std::wstring FormatColumn(const std::wstring& inputString, size_t length, Align align) { 566 | // First, ensure the input string does not have trailing null characters 567 | std::wstring trimmedInput = inputString; 568 | size_t nullPos = trimmedInput.find(L'\0'); 569 | if (nullPos != std::wstring::npos) { 570 | trimmedInput.resize(nullPos); // Remove trailing null characters 571 | } 572 | 573 | // Determine the number of padding characters needed 574 | size_t paddingLength = length > trimmedInput.length() ? length - trimmedInput.length() : 0; 575 | 576 | // Build the output string with appropriate padding 577 | std::wstring output; 578 | if (align == Align::Left) { 579 | output = trimmedInput + std::wstring(paddingLength, L' '); // Pad on the right 580 | } 581 | else { 582 | output = std::wstring(paddingLength, L' ') + trimmedInput; // Pad on the left 583 | } 584 | 585 | // Ensure the output is not longer than the specified length 586 | if (output.length() > length) { 587 | output.resize(length); 588 | } 589 | 590 | return output; 591 | } 592 | 593 | 594 | std::wstring GetLogicalName(DWORD DiskNumber, DWORD PartitionNumber, uint64_t PartitionOffset) { 595 | WCHAR volume_name[MAX_PATH] = { 0 }; 596 | HANDLE hVolume = FindFirstVolumeW(volume_name, ARRAYSIZE(volume_name)); 597 | 598 | if (hVolume == INVALID_HANDLE_VALUE) { 599 | std::wcerr << L"Could not access first GUID volume: " << GetLastError() << std::endl; 600 | return L""; 601 | } 602 | 603 | std::wstring volumeName; 604 | VOLUME_DISK_EXTENTS diskExtents; 605 | DWORD size = 0; 606 | 607 | do { 608 | size_t volumePathLength = wcslen(volume_name); 609 | if (volumePathLength <= 4 || volume_name[volumePathLength - 1] != L'\\') { 610 | continue; 611 | } 612 | 613 | // Safely convert size_t to DWORD after ensuring it's within range 614 | DWORD volumePathLengthDword = static_cast(volumePathLength); 615 | if (volumePathLengthDword != volumePathLength) { 616 | std::wcerr << L"Volume path length exceeds DWORD maximum." << std::endl; 617 | break; 618 | } 619 | 620 | // Remove trailing backslash to prepare for CreateFileW 621 | volume_name[volumePathLength - 1] = L'\0'; 622 | 623 | HANDLE hDrive = CreateFileW(volume_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 624 | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 625 | if (hDrive == INVALID_HANDLE_VALUE) { 626 | std::wcerr << L"Could not open GUID volume '" << volume_name << L"': " << GetLastError() << std::endl; 627 | continue; 628 | } 629 | 630 | BOOL result = DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &diskExtents, sizeof(diskExtents), &size, NULL); 631 | CloseHandle(hDrive); 632 | 633 | if (!result || size == 0) { 634 | //std::wcerr << L"Could not get Disk Extents for volume '" << volume_name << L"': " << GetLastError() << std::endl; 635 | // This means we need to go to Fallback logic 636 | continue; 637 | } 638 | else if (diskExtents.NumberOfDiskExtents == 1 && diskExtents.Extents[0].DiskNumber == DiskNumber && 639 | (PartitionOffset == 0 || diskExtents.Extents[0].StartingOffset.QuadPart == PartitionOffset)) { 640 | volumeName = volume_name; 641 | break; 642 | } 643 | 644 | } while (FindNextVolumeW(hVolume, volume_name, ARRAYSIZE(volume_name))); 645 | 646 | FindVolumeClose(hVolume); 647 | 648 | if (volumeName.empty()) { 649 | // Fallback: Use device paths directly if no volume name found 650 | WCHAR devicePath[MAX_PATH]; 651 | swprintf_s(devicePath, MAX_PATH, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u", DiskNumber, PartitionNumber); 652 | HANDLE hDrive = CreateFileW(devicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 653 | 654 | if (hDrive != INVALID_HANDLE_VALUE) { 655 | volumeName = devicePath; // Use the device path directly if available 656 | CloseHandle(hDrive); 657 | } 658 | } 659 | 660 | return volumeName; 661 | } 662 | 663 | 664 | bool MountPartition(DWORD DiskNumber, DWORD PartitionNumber, const std::wstring& driveLetter) { 665 | auto partitions = GetPartitionInfo(); 666 | auto partitionInfo = std::find_if(partitions.begin(), partitions.end(), [&](const PartitionInfo& p) { 667 | return p.DiskNumber == DiskNumber && p.PartitionNumber == PartitionNumber; 668 | }); 669 | 670 | if (partitionInfo == partitions.end()) { 671 | std::wcerr << L"Partition not found for Disk " << DiskNumber << L", Partition " << PartitionNumber << std::endl; 672 | return false; 673 | } 674 | 675 | std::wstring volumeName = GetLogicalName(DiskNumber, PartitionNumber, partitionInfo->PartitionOffset); 676 | 677 | if (volumeName.empty()) { 678 | std::wcerr << L"Could not get volume name for Disk " << DiskNumber << L", Partition " << PartitionNumber << std::endl; 679 | return false; 680 | } 681 | 682 | // Ensure drive letter is formatted correctly with a backslash 683 | std::wstring formattedDriveLetter = driveLetter; 684 | if (formattedDriveLetter.back() != L'\\') { 685 | formattedDriveLetter += L'\\'; 686 | } 687 | 688 | // Ensure volume name is correctly formatted for SetVolumeMountPointW 689 | std::wstring formattedVolumeName = volumeName; 690 | if (formattedVolumeName.back() != L'\\') { 691 | formattedVolumeName += L'\\'; 692 | } 693 | 694 | // Attempt to mount using SetVolumeMountPointW first 695 | if (SetVolumeMountPointW(formattedDriveLetter.c_str(), formattedVolumeName.c_str())) { 696 | //std::wcout << L"Successfully mounted " << volumeName << L" to " << formattedDriveLetter << L" using SetVolumeMountPointW." << std::endl; 697 | return true; 698 | } 699 | else { 700 | DWORD lastError = GetLastError(); 701 | //std::wcerr << L"Failed to mount " << volumeName << L" to " << formattedDriveLetter << L" using SetVolumeMountPointW. Error: " << lastError << std::endl; 702 | 703 | // If SetVolumeMountPointW fails, check if we should fallback to DefineDosDeviceW 704 | if (volumeName.find(L"\\\\?\\GLOBALROOT") == 0 || lastError == ERROR_INVALID_PARAMETER) { 705 | WCHAR dosName[] = L"?:"; 706 | dosName[0] = driveLetter[0]; 707 | 708 | // Remove any existing mapping for the drive letter to avoid conflicts 709 | DefineDosDeviceW(DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM, dosName, NULL); 710 | 711 | // Format volumeName correctly for DefineDosDeviceW (strip "\\?\GLOBALROOT") 712 | std::wstring devicePath = volumeName; 713 | if (devicePath.find(L"\\\\?\\GLOBALROOT") == 0) { 714 | devicePath = devicePath.substr(14); // Strip "\\?\GLOBALROOT" 715 | } 716 | 717 | // Attempt fallback mounting using DefineDosDeviceW 718 | if (DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM, dosName, devicePath.c_str())) { 719 | //std::wcout << volumeName << L" was successfully mounted as " << dosName << L" using DefineDosDeviceW." << std::endl; 720 | return true; 721 | } 722 | else { 723 | //std::wcerr << L"Could not mount " << volumeName << L" as " << dosName << L" using DefineDosDeviceW. Error: " << GetLastError() << std::endl; 724 | return false; 725 | } 726 | } 727 | } 728 | 729 | return false; 730 | } 731 | 732 | 733 | bool UnMountPartition(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber) { 734 | // Get the drive letter associated with the partition 735 | std::wstring volumeLetter = GetDriveLetter(partitions, diskNumber, partitionNumber); 736 | 737 | if (volumeLetter.empty()) { 738 | //std::wcerr << L"Drive letter not found for Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 739 | return false; 740 | } 741 | 742 | // Attempt to unmount using DeleteVolumeMountPointW 743 | if (DeleteVolumeMountPointW(volumeLetter.c_str())) { 744 | //std::wcout << L"Successfully unmounted " << volumeLetter << std::endl; 745 | return true; 746 | } 747 | else { 748 | //std::wcerr << L"Failed to unmount using DeleteVolumeMountPointW for " << volumeLetter << L". Error: " << GetLastError() << std::endl; 749 | 750 | // If DeleteVolumeMountPointW fails, use DefineDosDeviceW as a fallback 751 | WCHAR dosName[] = L"?:"; 752 | dosName[0] = volumeLetter[0]; // Set drive letter 753 | 754 | if (!DefineDosDeviceW(DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM, dosName, NULL)) { 755 | //std::wcerr << L"Failed to unmount using DefineDosDeviceW for " << dosName << L". Error: " << GetLastError() << std::endl; 756 | return false; 757 | } 758 | 759 | //std::wcout << L"Successfully unmounted '" << dosName << L"' using DefineDosDeviceW fallback." << std::endl; 760 | return true; 761 | } 762 | } 763 | 764 | 765 | bool UnMountPartitionByDriveLetter(const wchar_t* driveLetter) { 766 | std::wstring volumePath = driveLetter; 767 | 768 | // Ensure the drive letter ends with a backslash 769 | if (!volumePath.empty() && volumePath.back() != L'\\') { 770 | volumePath += L'\\'; 771 | } 772 | 773 | // Attempt to unmount using DeleteVolumeMountPointW 774 | if (DeleteVolumeMountPointW(volumePath.c_str())) { 775 | //std::wcout << L"Successfully unmounted drive " << driveLetter << std::endl; 776 | return true; 777 | } 778 | else { 779 | // If DeleteVolumeMountPointW fails, use DefineDosDeviceW as a fallback 780 | //std::wcerr << L"Failed to unmount using DeleteVolumeMountPointW for drive " << driveLetter << L". Error: " << GetLastError() << std::endl; 781 | 782 | // DefineDosDeviceW Fallback 783 | WCHAR dosName[] = L"?:"; 784 | dosName[0] = driveLetter[0]; // Set drive letter 785 | 786 | if (!DefineDosDeviceW(DDD_REMOVE_DEFINITION | DDD_NO_BROADCAST_SYSTEM, dosName, NULL)) { 787 | //std::wcerr << L"Failed to unmount using DefineDosDeviceW for drive " << dosName << L". Error: " << GetLastError() << std::endl; 788 | return false; 789 | } 790 | 791 | //std::wcout << L"Successfully unmounted '" << dosName << L"' using DefineDosDeviceW fallback." << std::endl; 792 | return true; 793 | } 794 | } 795 | 796 | 797 | // Function to unmount a partition if it is mounted, and return its drive letter if it was mounted 798 | std::wstring UnmountPartitionIfMounted(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber) { 799 | // First, check if the partition is mounted by looking up the drive letter 800 | std::wstring driveLetter = GetDriveLetter(partitions, diskNumber, partitionNumber); 801 | 802 | if (driveLetter.empty()) { 803 | // If no drive letter is found, the partition is likely not mounted 804 | //std::wcerr << L"Partition not mounted for Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 805 | return L""; 806 | } 807 | 808 | // Attempt to unmount using the existing UnMountPartition function 809 | if (UnMountPartition(partitions, diskNumber, partitionNumber)) { 810 | //std::wcout << L"Successfully unmounted Disk " << diskNumber << L", Partition " << partitionNumber << L" with drive letter " << driveLetter << std::endl; 811 | return driveLetter; // Return the drive letter that was unmounted 812 | } 813 | else { 814 | //std::wcerr << L"Failed to unmount Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 815 | return L""; // Return an empty string if the unmount failed 816 | } 817 | } 818 | 819 | 820 | // Function to get available drive letters 821 | std::vector GetAvailableDriveLetters() { 822 | // Buffer to hold the list of all defined DOS devices 823 | WCHAR deviceNames[65536] = { 0 }; // 64KB buffer 824 | DWORD charsReturned = QueryDosDeviceW(NULL, deviceNames, 65536); 825 | std::vector usedLetters; 826 | 827 | if (charsReturned != 0) { 828 | // Parse the buffer returned by QueryDosDevice 829 | WCHAR* device = deviceNames; 830 | while (*device) { 831 | if (wcslen(device) == 2 && device[1] == L':') { 832 | usedLetters.push_back(std::wstring(device)); // Collect used drive letters 833 | } 834 | device += wcslen(device) + 1; 835 | } 836 | } 837 | 838 | // Additionally, check volume mount points using GetVolumePathNamesForVolumeNameW 839 | WCHAR volumeName[MAX_PATH] = { 0 }; 840 | HANDLE hVolume = FindFirstVolumeW(volumeName, ARRAYSIZE(volumeName)); 841 | if (hVolume != INVALID_HANDLE_VALUE) { 842 | do { 843 | WCHAR volumePaths[MAX_PATH] = { 0 }; 844 | DWORD pathLength = 0; 845 | GetVolumePathNamesForVolumeNameW(volumeName, volumePaths, MAX_PATH, &pathLength); 846 | 847 | WCHAR* path = volumePaths; 848 | while (*path) { 849 | if (wcslen(path) == 3 && path[1] == L':' && path[2] == L'\\') { 850 | usedLetters.push_back(std::wstring(path, 2)); // Add the drive letter (e.g., "C:") 851 | } 852 | path += wcslen(path) + 1; 853 | } 854 | } while (FindNextVolumeW(hVolume, volumeName, ARRAYSIZE(volumeName))); 855 | 856 | FindVolumeClose(hVolume); 857 | } 858 | 859 | // List of available drive letters in reverse order (Z to C) 860 | std::vector availableLetters; 861 | for (wchar_t letter = L'Z'; letter >= L'C'; --letter) { 862 | std::wstring driveLetter = std::wstring(1, letter) + L":"; 863 | if (std::find(usedLetters.begin(), usedLetters.end(), driveLetter) == usedLetters.end()) { 864 | // Check also with GetDriveType to avoid conflicts with other types like floppy, CD/DVD, etc. 865 | WCHAR drivePath[] = L"X:\\"; 866 | drivePath[0] = letter; 867 | UINT driveType = GetDriveTypeW(drivePath); 868 | 869 | if (driveType == DRIVE_NO_ROOT_DIR) { // DRIVE_NO_ROOT_DIR means no volume is mounted to that letter 870 | availableLetters.push_back(driveLetter + L"\\"); // Add available drive letter 871 | } 872 | } 873 | } 874 | 875 | return availableLetters; 876 | } 877 | 878 | 879 | // Function to check if a partition is already mounted and return its drive letter if so 880 | std::wstring IsPartitionAlreadyMounted(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber) { 881 | for (const auto& partition : partitions) { 882 | if (partition.DiskNumber == diskNumber && partition.PartitionNumber == partitionNumber) { 883 | // Check if a drive letter is already assigned 884 | if (!partition.DriveLetter.empty()) { 885 | return partition.DriveLetter; // Partition is already mounted 886 | } 887 | } 888 | } 889 | 890 | return L""; // Partition is not mounted 891 | } 892 | 893 | 894 | bool SetLabel(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber, const std::wstring& newLabel) { 895 | // Retrieve the drive letter using your existing functions 896 | std::wstring driveLetter = GetDriveLetter(partitions, diskNumber, partitionNumber); 897 | 898 | bool wasMounted = !driveLetter.empty(); 899 | bool tempMount = false; 900 | 901 | if (!wasMounted) { 902 | // If the partition is not mounted, mount it temporarily 903 | std::vector availableLetters = GetAvailableDriveLetters(); 904 | if (!availableLetters.empty()) { 905 | driveLetter = availableLetters.front(); 906 | if (MountPartition(diskNumber, partitionNumber, driveLetter)) { 907 | //std::wcout << L"Temporarily mounted Disk " << diskNumber << L", Partition " << partitionNumber << L" to " << driveLetter << std::endl; 908 | tempMount = true; 909 | } 910 | else { 911 | //std::wcerr << L"Failed to temporarily mount Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 912 | return false; 913 | } 914 | } 915 | else { 916 | //std::wcerr << L"No available drive letters to mount Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 917 | return false; 918 | } 919 | } 920 | 921 | // Set the new label 922 | if (SetVolumeLabelW(driveLetter.c_str(), newLabel.c_str())) { 923 | //std::wcout << L"Label set to '" << newLabel << L"' for " << driveLetter << std::endl; 924 | if (tempMount) { 925 | UnMountPartitionByDriveLetter(driveLetter.c_str()); 926 | } 927 | return true; 928 | } 929 | else { 930 | //std::wcerr << L"Failed to set label for " << driveLetter << L". Error: " << GetLastError() << std::endl; 931 | if (tempMount) { 932 | UnMountPartitionByDriveLetter(driveLetter.c_str()); 933 | } 934 | return false; 935 | } 936 | } 937 | 938 | 939 | bool SetPartitionActiveDirectly(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber, BYTE setActiveValue = 0x80) { 940 | 941 | // Proceed only on MBR disk 942 | if (GetPartitionStyle(partitions, diskNumber, partitionNumber) == PARTITION_STYLE_MBR) { 943 | 944 | // Unmount the volume and get the drive letter if it was mounted 945 | std::wstring mountedDriveLetter = UnmountPartitionIfMounted(partitions, diskNumber, partitionNumber); 946 | 947 | 948 | // std::wcout << L"Setting partition " << partitionNumber << L" on Disk " << diskNumber << L" as active with value " << static_cast(setActiveValue) << L"..." << std::endl; 949 | 950 | // Open the disk 951 | std::wstring diskPath = L"\\\\.\\PhysicalDrive" + std::to_wstring(diskNumber); 952 | HANDLE hDisk = CreateFileW(diskPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 953 | if (hDisk == INVALID_HANDLE_VALUE) { 954 | std::wcerr << L"Failed to open disk. Error: " << GetLastError() << std::endl; 955 | return false; 956 | } 957 | 958 | // Read the MBR 959 | BYTE mbr[512]; 960 | DWORD bytesRead; 961 | if (!ReadFile(hDisk, mbr, sizeof(mbr), &bytesRead, NULL) || bytesRead != sizeof(mbr)) { 962 | std::wcerr << L"Failed to read MBR. Error: " << GetLastError() << std::endl; 963 | CloseHandle(hDisk); 964 | return false; 965 | } 966 | 967 | // Check and modify the active partition flag in the MBR 968 | bool found = false; 969 | for (int i = 0; i < 4; ++i) { 970 | int offset = 446 + i * 16; // Each partition entry is 16 bytes 971 | // The partition number is typically not stored in the MBR entry, so we need to count them 972 | if (partitionNumber == i + 1) { // Adjust if your numbering is different 973 | found = true; 974 | mbr[offset] = setActiveValue; // Set or clear the active flag 975 | break; 976 | } 977 | } 978 | 979 | if (!found) { 980 | std::wcerr << L"Partition not found in MBR or partition number mismatch." << std::endl; 981 | CloseHandle(hDisk); 982 | return false; 983 | } 984 | 985 | // Write the modified MBR back to the disk 986 | DWORD bytesWritten; 987 | SetFilePointer(hDisk, 0, NULL, FILE_BEGIN); 988 | if (!WriteFile(hDisk, mbr, sizeof(mbr), &bytesWritten, NULL) || bytesWritten != sizeof(mbr)) { 989 | std::wcerr << L"Failed to write modified MBR. Error: " << GetLastError() << std::endl; 990 | CloseHandle(hDisk); 991 | return false; 992 | } 993 | 994 | // Refresh the disk properties to ensure the system acknowledges the changes 995 | if (!DeviceIoControl(hDisk, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesWritten, NULL)) { 996 | std::wcerr << L"Failed to update disk properties. Error: " << GetLastError() << std::endl; 997 | CloseHandle(hDisk); 998 | return false; 999 | } 1000 | 1001 | // Introduce a short delay to allow the system to recognize the changes 1002 | Sleep(1000); // Wait for 1 second 1003 | 1004 | CloseHandle(hDisk); 1005 | 1006 | // Remount the volume if it was previously mounted 1007 | if (!mountedDriveLetter.empty()) { 1008 | MountPartition(diskNumber, partitionNumber, mountedDriveLetter); 1009 | } 1010 | 1011 | 1012 | std::wcout << L"Partition " << partitionNumber << " on Disk " << diskNumber << L" set as active with value 0x" << std::hex << static_cast(setActiveValue) << std::endl; 1013 | return true; 1014 | } 1015 | else { 1016 | std::wcout << L"This command is valid only for partition on MBR disk" << std::endl; 1017 | return false; 1018 | } 1019 | } 1020 | 1021 | 1022 | bool ChangePartitionTypeDirectly(const std::vector& partitions, DWORD diskNumber, DWORD partitionNumber, BYTE newPartitionType) { 1023 | 1024 | // Proceed only on MBR disk 1025 | if (GetPartitionStyle(partitions, diskNumber, partitionNumber) == PARTITION_STYLE_MBR) { 1026 | 1027 | // Unmount the volume and get the drive letter if it was mounted 1028 | std::wstring mountedDriveLetter = UnmountPartitionIfMounted(partitions, diskNumber, partitionNumber); 1029 | 1030 | 1031 | //std::wcout << L"Changing partition type for Partition " << partitionNumber << L" on Disk " << diskNumber << L" to 0x" << std::hex << static_cast(newPartitionType) << L"..." << std::endl; 1032 | 1033 | // Open the disk 1034 | std::wstring diskPath = L"\\\\.\\PhysicalDrive" + std::to_wstring(diskNumber); 1035 | HANDLE hDisk = CreateFileW(diskPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 1036 | if (hDisk == INVALID_HANDLE_VALUE) { 1037 | std::wcerr << L"Failed to open disk. Error: " << GetLastError() << std::endl; 1038 | return false; 1039 | } 1040 | 1041 | // Read the MBR 1042 | BYTE mbr[512]; 1043 | DWORD bytesRead; 1044 | if (!ReadFile(hDisk, mbr, sizeof(mbr), &bytesRead, NULL) || bytesRead != sizeof(mbr)) { 1045 | std::wcerr << L"Failed to read MBR. Error: " << GetLastError() << std::endl; 1046 | CloseHandle(hDisk); 1047 | return false; 1048 | } 1049 | 1050 | // Modify the partition type for the specified partition 1051 | bool found = false; 1052 | for (int i = 0; i < 4; ++i) { 1053 | int offset = 446 + i * 16; // Each partition entry is 16 bytes 1054 | // Check the partition number 1055 | if (partitionNumber == i + 1) { // Adjust if your numbering is different 1056 | found = true; 1057 | mbr[offset + 4] = newPartitionType; // 4th byte of the entry is the partition type 1058 | break; 1059 | } 1060 | } 1061 | 1062 | if (!found) { 1063 | std::wcerr << L"Partition not found in MBR or partition number mismatch." << std::endl; 1064 | CloseHandle(hDisk); 1065 | return false; 1066 | } 1067 | 1068 | // Write the modified MBR back to the disk 1069 | DWORD bytesWritten; 1070 | SetFilePointer(hDisk, 0, NULL, FILE_BEGIN); 1071 | if (!WriteFile(hDisk, mbr, sizeof(mbr), &bytesWritten, NULL) || bytesWritten != sizeof(mbr)) { 1072 | std::wcerr << L"Failed to write modified MBR. Error: " << GetLastError() << std::endl; 1073 | CloseHandle(hDisk); 1074 | return false; 1075 | } 1076 | 1077 | // Refresh the disk properties to ensure the system acknowledges the changes 1078 | if (!DeviceIoControl(hDisk, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesWritten, NULL)) { 1079 | std::wcerr << L"Failed to update disk properties. Error: " << GetLastError() << std::endl; 1080 | CloseHandle(hDisk); 1081 | return false; 1082 | } 1083 | 1084 | // Introduce a short delay to allow the system to recognize the changes 1085 | Sleep(1000); // Wait for 1 second 1086 | 1087 | CloseHandle(hDisk); 1088 | 1089 | // Remount the volume if it was previously mounted 1090 | if (!mountedDriveLetter.empty()) { 1091 | MountPartition(diskNumber, partitionNumber, mountedDriveLetter); 1092 | } 1093 | 1094 | 1095 | std::wcout << L"Partition type for Partition " << partitionNumber << L" on Disk " << diskNumber << L" changed to 0x" << std::hex << static_cast(newPartitionType) << std::endl; 1096 | return true; 1097 | } else { 1098 | std::wcout << L"This command is valid only for partition on MBR disk" << std::endl; 1099 | return false; 1100 | } 1101 | } 1102 | 1103 | 1104 | bool IsWindowsVersionGreaterOrEqual(DWORD majorVersion, DWORD minorVersion) { 1105 | HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); 1106 | if (hMod) { 1107 | RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); 1108 | if (fxPtr != nullptr) { 1109 | RTL_OSVERSIONINFOW rovi = { 0 }; 1110 | rovi.dwOSVersionInfoSize = sizeof(rovi); 1111 | if (STATUS_SUCCESS == fxPtr(&rovi)) { 1112 | if (rovi.dwMajorVersion > majorVersion) { 1113 | return true; 1114 | } 1115 | else if (rovi.dwMajorVersion == majorVersion && rovi.dwMinorVersion >= minorVersion) { 1116 | return true; 1117 | } 1118 | } 1119 | } 1120 | } 1121 | return false; 1122 | } 1123 | 1124 | 1125 | std::wstring ReplaceSpecialCharacters(const std::wstring& input) { 1126 | 1127 | // On Windows 8 and newer can be displayed UTF16 in console without issues 1128 | if (IsWindowsVersionGreaterOrEqual(6, 2)) { 1129 | return input; 1130 | } 1131 | 1132 | static const std::map replacements = { 1133 | // Latin (basic and extended) 1134 | {L'À', L'A'}, {L'Á', L'A'}, {L'Â', L'A'}, {L'Ã', L'A'}, {L'Ä', L'A'}, {L'Å', L'A'},/* {L'Æ', L'AE'}, */ 1135 | {L'à', L'a'}, {L'á', L'a'}, {L'â', L'a'}, {L'ã', L'a'}, {L'ä', L'a'}, {L'å', L'a'},/* {L'æ', L'ae'}, */ 1136 | {L'Ç', L'C'}, {L'ç', L'c'}, 1137 | {L'È', L'E'}, {L'É', L'E'}, {L'Ê', L'E'}, {L'Ë', L'E'}, 1138 | {L'è', L'e'}, {L'é', L'e'}, {L'ê', L'e'}, {L'ë', L'e'}, 1139 | {L'Ì', L'I'}, {L'Í', L'I'}, {L'Î', L'I'}, {L'Ï', L'I'}, 1140 | {L'ì', L'i'}, {L'í', L'i'}, {L'î', L'i'}, {L'ï', L'i'}, 1141 | {L'Ñ', L'N'}, {L'ñ', L'n'}, 1142 | {L'Ò', L'O'}, {L'Ó', L'O'}, {L'Ô', L'O'}, {L'Õ', L'O'}, {L'Ö', L'O'}, {L'Ø', L'O'}, 1143 | {L'ò', L'o'}, {L'ó', L'o'}, {L'ô', L'o'}, {L'õ', L'o'}, {L'ö', L'o'}, {L'ø', L'o'}, 1144 | /*{L'ß', L'ss'}, */ 1145 | {L'Ù', L'U'}, {L'Ú', L'U'}, {L'Û', L'U'}, {L'Ü', L'U'}, 1146 | {L'ù', L'u'}, {L'ú', L'u'}, {L'û', L'u'}, {L'ü', L'u'}, 1147 | {L'Ý', L'Y'}, {L'ý', L'y'}, {L'ÿ', L'y'}, 1148 | 1149 | // Slavic and Eastern European characters 1150 | {L'Ą', L'A'}, {L'ą', L'a'}, {L'Ć', L'C'}, {L'ć', L'c'}, 1151 | {L'Ę', L'E'}, {L'ę', L'e'}, {L'Ł', L'L'}, {L'ł', L'l'}, 1152 | {L'Ń', L'N'}, {L'ń', L'n'}, {L'Ó', L'O'}, {L'ó', L'o'}, 1153 | {L'Ś', L'S'}, {L'ś', L's'}, {L'Ź', L'Z'}, {L'ź', L'z'}, 1154 | {L'Ż', L'Z'}, {L'ż', L'z'}, 1155 | 1156 | // Additional characters 1157 | {L'Đ', L'D'}, {L'đ', L'd'}, {L'Ħ', L'H'}, {L'ħ', L'h'}, 1158 | {L'Ŀ', L'L'}, {L'ŀ', L'l'}, {L'Ł', L'L'}, {L'ł', L'l'}, 1159 | {L'Ŋ', L'N'}, {L'ŋ', L'n'}, /*{L'Œ', L'OE'}, {L'œ', L'oe'}, */ 1160 | {L'Š', L'S'}, {L'š', L's'}, {L'Ť', L'T'}, {L'ť', L't'}, 1161 | {L'Ž', L'Z'}, {L'ž', L'z'}, {L'Ə', L'A'}, {L'ə', L'a'}, 1162 | // ... (extend with more characters as needed) 1163 | 1164 | // Note: This list is not exhaustive and mainly covers characters with diacritics. 1165 | 1166 | }; 1167 | 1168 | std::wstring result; 1169 | for (wchar_t ch : input) { 1170 | auto it = replacements.find(ch); 1171 | if (it != replacements.end()) { 1172 | result += it->second; 1173 | } 1174 | else { 1175 | result += ch; 1176 | } 1177 | } 1178 | 1179 | return result; 1180 | } 1181 | 1182 | 1183 | bool IsRunningAsAdmin() { 1184 | bool isAdmin = false; 1185 | PSID adminGroupSID = NULL; 1186 | SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; 1187 | 1188 | // Allocate and initialize a SID for the administrators group. 1189 | if (!AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, 1190 | DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroupSID)) { 1191 | std::cerr << "AllocateAndInitializeSid Error: " << GetLastError() << std::endl; 1192 | return false; 1193 | } 1194 | 1195 | // Check if the SID is valid and if the user belongs to the admin group. 1196 | BOOL isInAdminGroup = FALSE; 1197 | if (!CheckTokenMembership(NULL, adminGroupSID, &isInAdminGroup)) { 1198 | std::cerr << "CheckTokenMembership Error: " << GetLastError() << std::endl; 1199 | } 1200 | else { 1201 | isAdmin = isInAdminGroup == TRUE; 1202 | } 1203 | 1204 | // Free the SID after use. 1205 | FreeSid(adminGroupSID); 1206 | 1207 | return isAdmin; 1208 | } 1209 | 1210 | 1211 | int main(int argc, char* argv[]) { 1212 | 1213 | if (!IsRunningAsAdmin()) { 1214 | std::wcerr << L"This tool needs to be launched with administrator privileges.\n"; 1215 | return 1; // Exit if not admin 1216 | } 1217 | 1218 | if (_isatty(_fileno(stdout))) { 1219 | if (_setmode(_fileno(stdout), _O_U16TEXT) == -1) { 1220 | std::wcerr << L"Failed to set the console output UTF16. Unicode output may not work correctly.\n"; 1221 | // Handle the error or exit if necessary 1222 | // exit(1); // Uncomment this line if you want to exit the program in case of an error 1223 | } 1224 | } 1225 | 1226 | 1227 | auto partitions = GetPartitionInfo(); 1228 | 1229 | if (argc > 1) { 1230 | std::string command = argv[1]; 1231 | 1232 | if (command == "/help" || command == "/?") { 1233 | // Handle the /help command 1234 | std::wcout << L"\n"; 1235 | std::wcout << L" ListDisk " << VER_PRODUCTVERSION_STR << "\n\n"; 1236 | std::wcout << L" This tool provides detailed information about connected physical disks, \n"; 1237 | std::wcout << L" including their partitions and volumes.\n"; 1238 | std::wcout << L"\n"; 1239 | std::wcout << L" Features include mounting/unmounting partitions, setting partition labels, \n"; 1240 | std::wcout << L" and modifying MBR partition attributes.\n"; 1241 | std::wcout << L"\n"; 1242 | std::wcout << L" Available Commands:\n"; 1243 | std::wcout << L" /mount - Mounts a partition to a specified drive letter.\n"; 1244 | std::wcout << L" /unmount - Unmounts a specified partition or drive letter.\n"; 1245 | std::wcout << L" /setlabel - Sets a new label for a specified partition.\n"; 1246 | std::wcout << L" /setactive - Sets or clears the bootable flag of a partition.\n"; 1247 | std::wcout << L" /settype - Changes the MBR type of a partition.\n"; 1248 | std::wcout << L"\n"; 1249 | std::wcout << L" Usage Examples:\n"; 1250 | std::wcout << L" /mount 0 1 - Mounts Partition 1 on Disk 0 to empty drive letter.\n"; 1251 | std::wcout << L" /mount 0 1 U - Mounts Partition 1 on Disk 0 as U: drive.\n"; 1252 | std::wcout << L" /unmount 0 1 - Unmounts Partition 1 on Disk 0.\n"; 1253 | std::wcout << L" /unmount U - Unmounts U: drive.\n"; 1254 | std::wcout << L" /setlabel 0 1 Lbl - Sets 'Lbl' as the label for Partition 1 on Disk 0.\n"; 1255 | std::wcout << L" /setactive 0 1 - Marks Partition 1 on Disk 0 as bootable.\n"; 1256 | std::wcout << L" /setactive 0 1 80 - Sets the boot flag for Partition 1 on Disk 0.\n"; 1257 | std::wcout << L" /setactive 0 1 00 - Clears the boot flag for Partition 1 on Disk 0.\n"; 1258 | std::wcout << L" /settype 0 1 0x17 - Sets Partition 1 on Disk 0 to type NTFS Hidden (0x17).\n"; 1259 | std::wcout << L" /settype 0 1 0x07 - Sets Partition 1 on Disk 0 to type NTFS (0x07).\n"; 1260 | std::wcout << L"\n"; 1261 | std::wcout << L" Note: Use this utility with caution. Incorrect usage may affect data integrity.\n"; 1262 | return 0; 1263 | } 1264 | else if (command == "/mount") { 1265 | // Handle the /mount command 1266 | if (argc < 4) { 1267 | std::wcout << " Usage: /mount \n"; 1268 | std::wcout << "Example: /mount 0 1 U - Mounts Partition 1 on Disk 0 as U: drive.\n"; 1269 | std::wcout << "Or: /mount 0 1 - Automatically mounts Partition 1 on Disk 0 to the first available drive letter.\n"; 1270 | return 1; 1271 | } 1272 | 1273 | int diskNumber = std::stoi(argv[2]); 1274 | int partitionNumber = std::stoi(argv[3]); 1275 | 1276 | // Check if the partition is already mounted 1277 | std::wstring currentDriveLetter = IsPartitionAlreadyMounted(partitions, diskNumber, partitionNumber); 1278 | if (!currentDriveLetter.empty()) { 1279 | //if (currentDriveLetter == requestedDriveLetter) { 1280 | std::wcout << L"Partition " << partitionNumber << L" on Disk " << diskNumber << L" is already mounted to " << currentDriveLetter << std::endl; 1281 | return 0; 1282 | //} 1283 | } 1284 | 1285 | std::wstring requestedDriveLetter; 1286 | 1287 | // Check if the user provided a drive letter 1288 | if (argc >= 5) { 1289 | requestedDriveLetter = argv[4][0]; // Convert char to wchar_t 1290 | requestedDriveLetter += L":\\"; // Append backslash as required by Windows API 1291 | } 1292 | else { 1293 | // No drive letter provided; find the first available drive letter 1294 | auto availableLetters = GetAvailableDriveLetters(); 1295 | if (!availableLetters.empty()) { 1296 | requestedDriveLetter = availableLetters.front(); 1297 | std::wcout << L"No drive letter specified. Automatically mounting to " << requestedDriveLetter << std::endl; 1298 | } 1299 | else { 1300 | std::wcout << L"No available drive letters found." << std::endl; 1301 | return 1; 1302 | } 1303 | } 1304 | 1305 | if (MountPartition(diskNumber, partitionNumber, requestedDriveLetter)) { 1306 | std::wcout << L"Successfully mounted Disk " << diskNumber << L", Partition " << partitionNumber << L" to drive letter " << requestedDriveLetter << std::endl; 1307 | } 1308 | else { 1309 | std::wcout << L"Failed to mount Disk " << diskNumber << L", Partition " << partitionNumber << L" to drive letter " << requestedDriveLetter << std::endl; 1310 | } 1311 | return 0; 1312 | } 1313 | else if (command == "/unmount") { 1314 | // Handle the /unmount command 1315 | if (argc < 3) { 1316 | std::wcout << " Usage: /unmount or /unmount \n"; 1317 | std::wcout << "Example: /unmount 0 1 - Unmounts Partition 1 on Disk 0.\n"; 1318 | std::wcout << "Example: /unmount U - Unmounts U: drive.\n"; 1319 | return 1; 1320 | } 1321 | 1322 | std::string target = argv[2]; 1323 | if (target.size() == 1 && isalpha(target[0])) { 1324 | // Unmount by drive letter 1325 | char driveLetterChar = target[0]; 1326 | std::wstring driveLetter = L""; 1327 | driveLetter += (wchar_t)driveLetterChar; 1328 | driveLetter += L":\\"; 1329 | 1330 | if (UnMountPartitionByDriveLetter(driveLetter.c_str())) { 1331 | std::wcout << "Successuflly unmounted partition assigned to drive letter " << driveLetterChar << ":\\" << std::endl; 1332 | } 1333 | else { 1334 | std::wcout << "Failed to unmount partition assigned to drive letter " << driveLetterChar << ":\\" << std::endl; 1335 | } 1336 | 1337 | } 1338 | else { 1339 | // Unmount by disk number and partition number 1340 | int diskNumber = std::stoi(target); 1341 | int partitionNumber = argc > 3 ? std::stoi(argv[3]) : 0; // Default to 0 if not provided 1342 | 1343 | if (UnMountPartition(partitions, diskNumber, partitionNumber)) { 1344 | std::wcout << "Successfully unmounted Disk " << diskNumber << ", Partition " << partitionNumber << std::endl; 1345 | } 1346 | else { 1347 | std::wcout << "Failed to unmount Disk " << diskNumber << ", Partition " << partitionNumber << std::endl; 1348 | } 1349 | } 1350 | 1351 | return 0; 1352 | } 1353 | else if (command == "/setactive") { 1354 | if (argc < 4 || argc > 5) { 1355 | std::wcout << " Usage: /settype \n"; 1356 | std::wcout << "Example: /settype 0 1 0x17 - Sets Partition 1 on Disk 0 to NTFS Hidden (0x17).\n"; 1357 | return 1; 1358 | } 1359 | 1360 | int diskNumber = std::stoi(argv[2]); 1361 | int partitionNumber = std::stoi(argv[3]); 1362 | BYTE setActiveValue = 0x80; // Default value 1363 | 1364 | // Check if the active value is provided 1365 | if (argc == 5) { 1366 | setActiveValue = static_cast(std::stoi(argv[4], nullptr, 16)); // Parse as hexadecimal 1367 | } 1368 | 1369 | SetPartitionActiveDirectly(partitions, diskNumber, partitionNumber, setActiveValue); 1370 | 1371 | return 0; 1372 | } 1373 | else if (command == "/settype") { 1374 | if (argc != 5) { 1375 | std::wcout << "Usage: /settype \n"; 1376 | std::wcout << "Example: /settype 0 1 0x07 - Sets Partition 1 on Disk 0 to NTFS (0x07).\n"; 1377 | 1378 | return 1; 1379 | } 1380 | 1381 | int diskNumber = std::stoi(argv[2]); 1382 | int partitionNumber = std::stoi(argv[3]); 1383 | BYTE newPartitionType = static_cast(std::stoi(argv[4], nullptr, 16)); // Correctly parse as hexadecimal 1384 | 1385 | ChangePartitionTypeDirectly(partitions, diskNumber, partitionNumber, newPartitionType); 1386 | 1387 | return 0; 1388 | } 1389 | else if (command == "/setlabel") { 1390 | // Handle the /setlabel command 1391 | if (argc < 5) { 1392 | std::wcout << L"Usage: /setlabel \n"; 1393 | std::wcout << L"Example: /setlabel 0 1 NewLabel - Sets 'NewLabel' as the label for Partition 1 on Disk 0.\n"; 1394 | return 1; 1395 | } 1396 | 1397 | int diskNumber = std::stoi(argv[2]); 1398 | int partitionNumber = std::stoi(argv[3]); 1399 | std::string newLabelUtf8 = argv[4]; 1400 | 1401 | // Convert UTF-8 to wide string 1402 | std::wstring newLabel(newLabelUtf8.begin(), newLabelUtf8.end()); 1403 | 1404 | if (SetLabel(partitions, diskNumber, partitionNumber, newLabel)) { 1405 | std::wcout << L"Label set to '" << newLabel << L"' for Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 1406 | } 1407 | else { 1408 | std::wcout << L"Failed to set label for Disk " << diskNumber << L", Partition " << partitionNumber << std::endl; 1409 | } 1410 | 1411 | return 0; 1412 | } 1413 | else { 1414 | std::wcout << "Unknown command: " << std::wstring(command.begin(), command.end()) << "\n"; 1415 | std::wcout << "Use /? or /help for a list of available commands.\n"; 1416 | return 1; 1417 | } 1418 | } 1419 | 1420 | 1421 | // Print header 1422 | std::wcout << L"\n| Disk " 1423 | << L"| Partition " 1424 | << L"| Style " 1425 | << L"| Size " 1426 | << L"| Type " 1427 | << L"| System " 1428 | << L"| Ltr " 1429 | << L"| Label |"; 1430 | std::wcout << std::endl; 1431 | 1432 | // Add an empty line as a separator 1433 | std::wcout << L" --------- ------------- ------- ------------ --------------- -------- ----- ------------------------" << std::endl; 1434 | 1435 | for (const auto& partition : partitions) { 1436 | std::wcout << FormatColumn(L"| Disk " + std::to_wstring(partition.DiskNumber), 9, Align::Left) << L" " 1437 | << FormatColumn(L"| Partition " + std::to_wstring(partition.PartitionNumber), 13, Align::Left) << L" " 1438 | << L"| " << FormatColumn(PartitionStyleToWString(partition.PartitionStyle), 5, Align::Left) << L" " 1439 | << L"| " << FormatSize(partition.PartitionLength) << L" " 1440 | << L"| " << FormatColumn(partition.PartitionType, 13, Align::Left) << L" " 1441 | << L"| " << FormatColumn(partition.FileSystem, 7, Align::Left) 1442 | << L"| " << FormatColumn(FormatLetter(partition.DriveLetter), 3, Align::Left) << L" " 1443 | //<< L"| " << partition.MountPointName 1444 | << L"| " << FormatColumn(ReplaceSpecialCharacters(partition.VolumeName), 22, Align::Left) << L" |"; 1445 | 1446 | std::wcout << std::endl; 1447 | } 1448 | 1449 | return 0; 1450 | } 1451 | -------------------------------------------------------------------------------- /ListDisk/ListDisk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: ListDisk.h 3 | */ 4 | 5 | #ifndef __LISTDISK_H__ 6 | #define __LISTDISK_H__ 7 | 8 | #define VER_FILEVERSION 1,1,0,0 9 | #define VER_FILEVERSION_STR "1.1\0" 10 | 11 | #define VER_PRODUCTVERSION 1,1,0,0 12 | #define VER_PRODUCTVERSION_STR "1.1\0" 13 | 14 | #endif /* __LISTDISK_H__ */ 15 | -------------------------------------------------------------------------------- /ListDisk/ListDisk.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeorgeK1ng/ListDisk/ec1ecff04bdba10e61ee996246ad22ad94200b40/ListDisk/ListDisk.ico -------------------------------------------------------------------------------- /ListDisk/ListDisk.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ListDisk.h" 3 | 4 | VS_VERSION_INFO VERSIONINFO 5 | FILEVERSION VER_FILEVERSION 6 | PRODUCTVERSION VER_PRODUCTVERSION 7 | FILEFLAGSMASK 0x3fL 8 | FILEFLAGS 0x0L 9 | FILEOS VOS__WINDOWS32 10 | FILETYPE VFT_APP 11 | FILESUBTYPE VFT2_UNKNOWN 12 | BEGIN 13 | BLOCK "StringFileInfo" 14 | BEGIN 15 | BLOCK "000004B0" // Language Neutral 16 | BEGIN 17 | VALUE "CompanyName", "George King Company\0" 18 | VALUE "FileDescription", "ListDisk\0" 19 | VALUE "FileVersion", VER_FILEVERSION_STR 20 | VALUE "InternalName", "ListDisk\0" 21 | VALUE "LegalCopyright", "Copyright © 2024\0" 22 | VALUE "OriginalFilename", "ListDisk.exe\0" 23 | VALUE "ProductName", "ListDisk\0" 24 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 25 | END 26 | END 27 | BLOCK "VarFileInfo" 28 | BEGIN 29 | VALUE "Translation", 0x0, 1200 // Language Neutral 30 | END 31 | END 32 | 33 | // Icon resource 34 | IDI_ICON1 ICON "ListDisk.ico" -------------------------------------------------------------------------------- /ListDisk/ListDisk.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release XP SSE 10 | Win32 11 | 12 | 13 | Release XP SSE 14 | x64 15 | 16 | 17 | Release XP 18 | Win32 19 | 20 | 21 | Release XP 22 | x64 23 | 24 | 25 | Release 26 | Win32 27 | 28 | 29 | Debug 30 | x64 31 | 32 | 33 | Release 34 | x64 35 | 36 | 37 | 38 | 16.0 39 | Win32Proj 40 | {e90bd5b9-2d7b-4dae-aa74-a7fa3c177258} 41 | ListDisk 42 | ListDisk 43 | 10.0 44 | 45 | 46 | 47 | 48 | Application 49 | true 50 | v141_xp 51 | Unicode 52 | false 53 | 54 | 55 | Application 56 | false 57 | v141_xp 58 | true 59 | Unicode 60 | false 61 | 62 | 63 | Application 64 | false 65 | v141_xp 66 | true 67 | Unicode 68 | false 69 | 70 | 71 | Application 72 | false 73 | v141_xp 74 | true 75 | Unicode 76 | false 77 | 78 | 79 | Application 80 | true 81 | v141_xp 82 | Unicode 83 | false 84 | 85 | 86 | Application 87 | false 88 | v141_xp 89 | true 90 | Unicode 91 | false 92 | 93 | 94 | Application 95 | false 96 | v141_xp 97 | true 98 | Unicode 99 | false 100 | 101 | 102 | Application 103 | false 104 | v141_xp 105 | true 106 | Unicode 107 | false 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | true 143 | $(SolutionDir)Release\x86\$(Configuration)\ 144 | $(SolutionDir)BuildOutput\x86\$(Configuration)\obj\ 145 | 146 | 147 | false 148 | $(SolutionDir)Release\x86\$(Configuration)\ 149 | $(SolutionDir)BuildOutput\x86\$(Configuration)\ 150 | 151 | 152 | false 153 | $(SolutionDir)Release\x86\$(Configuration)\ 154 | $(SolutionDir)BuildOutput\x86\$(Configuration)\ 155 | 156 | 157 | false 158 | $(SolutionDir)Release\x86\$(Configuration)\ 159 | $(SolutionDir)BuildOutput\x86\$(Configuration)\ 160 | 161 | 162 | true 163 | $(SolutionDir)Release\x64\$(Configuration)\ 164 | $(SolutionDir)BuildOutput\x64\$(Configuration)\ 165 | 166 | 167 | false 168 | $(SolutionDir)Release\x64\$(Configuration)\ 169 | $(SolutionDir)BuildOutput\x64\$(Configuration)\ 170 | 171 | 172 | false 173 | $(SolutionDir)Release\x64\$(Configuration)\ 174 | $(SolutionDir)BuildOutput\x64\$(Configuration)\ 175 | 176 | 177 | false 178 | $(SolutionDir)Release\x64\$(Configuration)\ 179 | $(SolutionDir)BuildOutput\x64\$(Configuration)\ 180 | 181 | 182 | 183 | 184 | Level3 185 | true 186 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 187 | true 188 | MultiThreadedDebug 189 | 190 | 191 | Console 192 | true 193 | 5.01 194 | 195 | 196 | 197 | 198 | Level3 199 | true 200 | true 201 | true 202 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 203 | true 204 | MultiThreaded 205 | 206 | 207 | Console 208 | true 209 | true 210 | true 211 | 5.02 212 | 213 | 214 | 215 | 216 | Level3 217 | true 218 | true 219 | true 220 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 221 | true 222 | MultiThreadedDLL 223 | 224 | 225 | Console 226 | true 227 | true 228 | true 229 | 5.01 230 | 231 | 232 | 233 | 234 | Level3 235 | true 236 | true 237 | true 238 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 239 | true 240 | MultiThreadedDLL 241 | StreamingSIMDExtensions 242 | 243 | 244 | Console 245 | true 246 | true 247 | true 248 | 5.01 249 | 250 | 251 | 252 | 253 | Level3 254 | true 255 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 256 | true 257 | MultiThreadedDebug 258 | 259 | 260 | Console 261 | true 262 | 5.02 263 | 264 | 265 | 266 | 267 | Level3 268 | true 269 | true 270 | true 271 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 272 | true 273 | MultiThreaded 274 | 275 | 276 | Console 277 | true 278 | true 279 | true 280 | 5.02 281 | 282 | 283 | 284 | 285 | Level3 286 | true 287 | true 288 | true 289 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 290 | true 291 | MultiThreadedDLL 292 | 293 | 294 | Console 295 | true 296 | true 297 | true 298 | 5.02 299 | 300 | 301 | 302 | 303 | Level3 304 | true 305 | true 306 | true 307 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 308 | true 309 | MultiThreadedDLL 310 | StreamingSIMDExtensions 311 | 312 | 313 | Console 314 | true 315 | true 316 | true 317 | 5.02 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | -------------------------------------------------------------------------------- /ListDisk/ListDisk.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 10 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | 19 | 20 | Resource Files 21 | 22 | 23 | -------------------------------------------------------------------------------- /ListDisk/ListDisk.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ListDisk 2 | 3 | This tool provides detailed information about connected physical disks, including their partitions and volumes. 4 | 5 | ## Features 6 | - Mounting/unmounting partitions 7 | - Setting partition labels 8 | - Modifying MBR partition attributes 9 | 10 | ## Available Commands 11 | - `/mount` - Mounts a partition to a specified drive letter. 12 | - `/unmount` - Unmounts a specified partition or drive letter. 13 | - `/setlabel` - Sets a new label for a specified partition. 14 | - `/setactive` - Sets or clears the bootable flag of a partition. 15 | - `/settype` - Changes the MBR type of a partition. 16 | 17 | ## Usage Examples 18 | - `/mount 0 1 U` - Mounts Partition 1 on Disk 0 as U: drive. 19 | - `/unmount 0 1` - Unmounts Partition 1 on Disk 0. 20 | - `/unmount U` - Unmounts U: drive. 21 | - `/setLabel 0 1 Lbl` - Sets 'Lbl' as the label for Partition 1 on Disk 0. 22 | - `/setactive 0 1` - Marks Partition 1 on Disk 0 as bootable. 23 | - `/setactive 0 1 80` - Sets the boot flag for Partition 1 on Disk 0. 24 | - `/setactive 0 1 00` - Clears the boot flag for Partition 1 on Disk 0. 25 | - `/settype 0 1 0x17` - Sets Partition 1 on Disk 0 to type NTFS Hidden (0x17). 26 | - `/settype 0 1 0x07` - Sets Partition 1 on Disk 0 to type NTFS (0x07). 27 | 28 | ## Requirements 29 | - **Operating System:** Windows XP and newer. 30 | - **Note for Windows XP Users:** This tool requires the installation of the Microsoft Visual C++ Redistributable. Please install it from [this link](https://github.com/abbodi1406/vcredist/releases/tag/v0.35.0). 31 | - **Compatible with Windows Preinstallation Environment (WinPE).** 32 | - **Place ListDisk in %WinDir%\system32 to make it easy accessible from commandline!** 33 | 34 | 35 | > **Note:** Use this utility with caution. Incorrect usage may affect data integrity. 36 | 37 | ![image](https://github.com/GeorgeK1ng/ListDisk/assets/98261225/bcdf04ba-070b-4ec3-846f-9b1a5fcbc8ed) 38 | 39 | ![image](https://github.com/GeorgeK1ng/ListDisk/assets/98261225/af4299e5-412d-42e5-9cee-b111e785cc1b) 40 | 41 | ![image](https://github.com/GeorgeK1ng/ListDisk/assets/98261225/b850d681-b6da-4bb3-88a8-29ad4c1fd104) 42 | 43 | ![image](https://github.com/GeorgeK1ng/ListDisk/assets/98261225/038d9428-523d-4bb7-8e9d-c025e055a25e) 44 | 45 | --------------------------------------------------------------------------------