potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++&Win API ドライブのジオメトリ情報(物理的なサイズ情報)の取得(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX)

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を用いて各構造体分のバッファーを確保しています。