DeviceIoControl関数を用いてドライブの物理ディスクジオメトリ情報(物理的なサイズ情報)を取得するサンプルコードです。この投稿ではIOCTL_DISK_GET_DRIVE_GEOMETRY_EXを用いた場合を紹介します。詳細な説明はいつか。
サンプルコード
#include <bit> #include <string> #include <string_view> #include <sstream> #include <memory> #include <vector> #include <iostream> #define STRICT #include <Windows.h> struct HANDLE_deleter { inline void operator()(HANDLE h) const noexcept { ::CloseHandle(h); } }; using unique_HANDLE = std::unique_ptr<std::remove_pointer_t<HANDLE>, HANDLE_deleter>; class DiskGeometry { public: DiskGeometry(wchar_t driveLetter) { wchar_t path[7]{ L"\\\\.\\X:" }; path[4] = driveLetter; unique_HANDLE volumeHandle = unique_HANDLE( ::CreateFileW(path, 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr)); if (volumeHandle.get() == INVALID_HANDLE_VALUE) return; m_buffer.resize(sizeof(DISK_GEOMETRY_EX) + sizeof(DISK_PARTITION_INFO) + sizeof(DISK_DETECTION_INFO)); DWORD returnedBytes{ 0 }; auto succeeded = ::DeviceIoControl( volumeHandle.get(), IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, m_buffer.data(), static_cast<DWORD>(m_buffer.size()), &returnedBytes, nullptr); if (!succeeded) m_buffer.clear(); } DiskGeometry(const DiskGeometry& source) : m_buffer(source.m_buffer) { } DiskGeometry(DiskGeometry&& source) : m_buffer(std::move(source.m_buffer)) { } constexpr const DISK_GEOMETRY_EX& GetDiskGeometryEx() const noexcept { return *std::bit_cast<const DISK_GEOMETRY_EX*>(m_buffer.data()); } constexpr const DISK_PARTITION_INFO& GetPartitionInfo() const noexcept { return *DiskGeometryGetPartition(&GetDiskGeometryEx()); } constexpr const DISK_DETECTION_INFO& GetDetectionInfo() const noexcept { return *DiskGeometryGetDetect(&GetDiskGeometryEx()); } private: std::vector<BYTE> m_buffer; }; std::wstring_view GetPartitionStyleName(const DISK_PARTITION_INFO& partationInfo) noexcept; std::wstring_view GetDetectionTypeName(const DISK_DETECTION_INFO& detectionInfo) noexcept; std::wstring GUIDToWString(const GUID& guid) noexcept; int wmain() { DiskGeometry diskGeometryForC('C'); // 各情報の参照 auto diskGeometry = diskGeometryForC.GetDiskGeometryEx(); auto partitionInfo = diskGeometryForC.GetPartitionInfo(); auto detectionInfo = diskGeometryForC.GetDetectionInfo(); // 出力 std::wstringstream output; // 全般情報 output << L"■ジオメトリ情報" << std::endl; output << L"(省略)" << std::endl; output << L"ディスク当たりのバイト数: " << diskGeometry.DiskSize.QuadPart << std::endl; // パーティション情報 output << L"□パーティション" << std::endl; output << L"スタイル: " << GetPartitionStyleName(partitionInfo) << L" (" << partitionInfo.PartitionStyle << L")" << std::endl; switch (partitionInfo.PartitionStyle) { case PARTITION_STYLE::PARTITION_STYLE_MBR: output << L"Signature: " << partitionInfo.Mbr.Signature << std::endl; output << L"CheckSum: " << partitionInfo.Mbr.CheckSum << std::endl; break; case PARTITION_STYLE::PARTITION_STYLE_GPT: output << L"DiskId: " << GUIDToWString(partitionInfo.Gpt.DiskId) << std::endl; break; case PARTITION_STYLE::PARTITION_STYLE_RAW: break; } // ディテクション情報 output << L"□ディテクション" << std::endl; output << L"型: " << GetDetectionTypeName(detectionInfo) << L" (" << detectionInfo.DetectionType << L")" << std::endl; switch (detectionInfo.DetectionType) { case DETECTION_TYPE::DetectNone: break; case DETECTION_TYPE::DetectInt13: output << L"DriveSelect: " << detectionInfo.Int13.DriveSelect << std::endl; output << L"ヘッド当たりのシリンジの最大数: " << detectionInfo.Int13.MaxCylinders << std::endl; output << L"トラック当たりのセクタ数: " << detectionInfo.Int13.SectorsPerTrack << std::endl; output << L"ディスクのヘッド数: " << detectionInfo.Int13.MaxHeads << std::endl; output << L"ドライブ数: " << detectionInfo.Int13.NumberDrives << std::endl; break; case DETECTION_TYPE::DetectExInt13: output << L"拡張ドライブパラメーターのバッファサイズ: " << detectionInfo.ExInt13.ExBufferSize << std::endl; output << L"フラグ: " << detectionInfo.ExInt13.ExFlags << std::endl; output << L"シリンダの数: " << detectionInfo.ExInt13.ExCylinders << std::endl; output << L"ヘッドの最大数: " << detectionInfo.ExInt13.ExHeads << std::endl; output << L"トラック当たりのセクタ数: " << detectionInfo.ExInt13.ExSectorsPerTrack << std::endl; output << L"セクタの数: " << detectionInfo.ExInt13.ExSectorsPerDrive << std::endl; output << L"セクタのサイズ: " << detectionInfo.ExInt13.ExSectorSize << std::endl; output << L"予約: " << detectionInfo.ExInt13.ExReserved << std::endl; break; } std::wcout.imbue(std::locale("", std::locale::ctype)); std::wcout << output.str(); return 0; } std::wstring_view GetPartitionStyleName(const DISK_PARTITION_INFO& partationInfo) noexcept { switch (partationInfo.PartitionStyle) { case PARTITION_STYLE::PARTITION_STYLE_MBR: return L"MBR"; case PARTITION_STYLE::PARTITION_STYLE_GPT: return L"GPT"; case PARTITION_STYLE::PARTITION_STYLE_RAW: return L"RAW"; default: return {}; } } std::wstring_view GetDetectionTypeName(const DISK_DETECTION_INFO& detectionInfo) noexcept { switch (detectionInfo.DetectionType) { case DETECTION_TYPE::DetectNone: return L"None"; case DETECTION_TYPE::DetectInt13: return L"Int13"; case DETECTION_TYPE::DetectExInt13: return L"ExInt13"; default: return {}; } } std::wstring GUIDToWString(const GUID& guid) noexcept { std::wstring s(38, L'\0'); if (FAILED(::StringFromGUID2(guid, s.data(), 39))) return {}; return std::move(s); }
メモ
DISK_GEOMETRY_EX構造体の確保方法
DISK_GEOMETRY_EX構造体は末尾にBYTE Data[1]を含む構造体として定義されています。DiskGeometryGetPartitionマクロがこのDataを明示的に型変換してDISK_PARTITION_INFOへのポインタを取得するように実際にはDISK_GEOMETRY_EX構造体のサイズは可変です(厳密にはNTDDI_VERSION < NTDDI_WS03
の場合)。
#define DiskGeometryGetPartition(Geometry)\ ((PDISK_PARTITION_INFO)((Geometry)->Data))
単純にDISK_GEOMETRY_EX構造体を定義してそのサイズをDeviceIoControl関数に渡すとDISK_GEOMETRY_EXには最小限の情報がセットされます。
実際にはDISK_PARTITION_INFO構造体やDISK_DETECTION_INFO構造体を含むことのできる十分な大きさのバッファを確保してDeviceIoControl関数を呼び出すことでそれらの情報を取得することができます。
上のサンプルコードではstd::vector