C++20におけるWinAPIのDeviceIoControl
(FSCTL_FILESYSTEM_GET_STATISTICS_EX
)のラッパークラスです。ファイルは本体であるDeviceIoControlForFileSystemStatisticsEx
を定義するDeviceIoControlForFileSystemStatisticsEx.hpp
、再利用可能そうなコードを集めたUtility.hpp
、プログラムの開始関数を含むmain.cpp
に分割しています。
DeviceIoControlForFileSystemStatisticsEx.hpp
#pragma once #include <system_error> #include <span> #include <vector> class DeviceIoControlForFileSystemStatisticsEx { public: DeviceIoControlForFileSystemStatisticsEx(HANDLE hFile) { if (hFile == INVALID_HANDLE_VALUE) { throw_system_error(); } data.resize(sizeof(FILESYSTEM_STATISTICS_EX)); DWORD bytes_returned; // data.size()はDWORDの範囲に収まるため、static_castを使用。 if (!::DeviceIoControl(hFile, FSCTL_FILESYSTEM_GET_STATISTICS_EX, nullptr, 0, data.data(), static_cast<DWORD>(data.size()), &bytes_returned, nullptr)) { if (::GetLastError() != ERROR_MORE_DATA) { data.clear(); throw_system_error(); } } data.resize(reinterpret_cast<FILESYSTEM_STATISTICS_EX*>(data.data())->SizeOfCompleteStructure); if (!::DeviceIoControl(hFile, FSCTL_FILESYSTEM_GET_STATISTICS_EX, nullptr, 0, data.data(), static_cast<DWORD>(data.size()), &bytes_returned, nullptr)) { data.clear(); throw_system_error(); } } std::span<const std::byte> data_complete() const noexcept { return std::span(data); } std::span<const std::byte> data_specified() const noexcept { return std::span<const std::byte>( std::next(data.cbegin(), sizeof(FILESYSTEM_STATISTICS_EX)), data.cend()); } const FILESYSTEM_STATISTICS_EX& filesystem_stats_ex() const noexcept { return *reinterpret_cast<const FILESYSTEM_STATISTICS_EX*>(data.data()); } DWORD size_complete() const noexcept { return filesystem_stats_ex().SizeOfCompleteStructure; } DWORD size_specified() const noexcept { return filesystem_stats_ex().SizeOfCompleteStructure - sizeof(FILESYSTEM_STATISTICS_EX); } private: template <DWORD TYPE, typename STRUCT> size_t specific_stats_size() const noexcept { const auto& fs_stats = filesystem_stats_ex(); if (fs_stats.FileSystemType != TYPE) { return 0; } return size_specified() / sizeof(STRUCT); } static void throw_system_error(int code = GetLastError()) { throw std::system_error(code, std::system_category()); } public: size_t ntfs_stats_ex_size() const noexcept { return specific_stats_size<FILESYSTEM_STATISTICS_TYPE_NTFS, NTFS_STATISTICS_EX>(); } size_t fat_stats_size() const noexcept { return specific_stats_size<FILESYSTEM_STATISTICS_TYPE_FAT, FAT_STATISTICS>(); } size_t exfat_stats_size() const noexcept { return specific_stats_size<FILESYSTEM_STATISTICS_TYPE_EXFAT, EXFAT_STATISTICS>(); } private: template <typename STRUCT> std::span<const STRUCT> specific_stats_ex(size_t size) const noexcept { auto begin = reinterpret_cast<const STRUCT*>(data_specified().data()); return std::span<const STRUCT>(begin, size); } public: std::span<const NTFS_STATISTICS_EX> ntfs_stats_ex() const noexcept { return specific_stats_ex<NTFS_STATISTICS_EX>(ntfs_stats_ex_size()); } std::span<const FAT_STATISTICS> fat_stats() const noexcept { return specific_stats_ex<FAT_STATISTICS>(fat_stats_size()); } std::span<const EXFAT_STATISTICS> exfat_stats() const noexcept { return specific_stats_ex<EXFAT_STATISTICS>(exfat_stats_size()); } private: std::vector<std::byte> data; // デフォルト・コピー・ムーブコンストラクタを許可しない。 DeviceIoControlForFileSystemStatisticsEx() = delete; DeviceIoControlForFileSystemStatisticsEx(const DeviceIoControlForFileSystemStatisticsEx&) = delete; DeviceIoControlForFileSystemStatisticsEx(DeviceIoControlForFileSystemStatisticsEx&&) = delete; };
main.cpp
#define STRICT #define NOMINMAX #include <Windows.h> #include "DeviceIoControlForFileSystemStatisticsEx.hpp" #include "Utility.hpp" int main() { for (wchar_t letter : GetLogicalDriveLetters()) { auto file_handle = CreateFileForVolume(letter); if (file_handle.get() == INVALID_HANDLE_VALUE) { continue; } auto fs_stats = DeviceIoControlForFileSystemStatisticsEx(file_handle.get()); auto ntfs_stats = fs_stats.ntfs_stats_ex(); auto fat_stats = fs_stats.fat_stats(); auto exfat_stats = fs_stats.exfat_stats(); } }
Utility.hpp
#pragma once #include <iostream> #include <memory> using namespace std; using unique_file_handle = unique_ptr<remove_pointer_t<HANDLE>, decltype(&CloseHandle)>; inline auto make_unique_file_handle(HANDLE handle) noexcept { return unique_file_handle(handle, &CloseHandle); } unique_file_handle CreateFileForVolume( wchar_t letter, DWORD desired_access = GENERIC_READ, DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) noexcept { wchar_t path[3]{ letter, L':', L'\0' }; return make_unique_file_handle(::CreateFileW( path, desired_access, share_mode, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr)); } template <bool UpperCase = true> vector<wchar_t> GetLogicalDriveLetters() { vector<wchar_t> letters; DWORD dw = ::GetLogicalDrives(); for (DWORD i = 0; i < 32; i++) { if (dw & (1 << i)) { if constexpr (UpperCase) { letters.emplace_back(static_cast<wchar_t>(L'A' + i)); } else { letters.emplace_back(static_cast<wchar_t>(L'a' + i)); } } } return move(letters); }