Win32 APIのDeviceIoControl
関数とIOCTL_STORAGE_QUERY_PROPERTY
フラグを使ってパスを含むドライブがSSD等か確認するサンプルコードです。オリジナルのソースコードはMicrosoft BlogのThe Old New ThingでRaymond Chen氏が公開されている記事であり、ここにSTLコンテナを加えただけのものです。アイデアの権利はRaymond Chen氏にあります。
#include <array> #include <string> #define STRICT #define NOMINMAX #include <windows.h> // WIL 1.0.201120.3 #include <wil/resource.h> // ファイルパスからボリューム名を取得します。 std::wstring GetVolumePathNameW(LPCWSTR path) noexcept(false) { THROW_IF_NULL_ALLOC(path); std::array<WCHAR, MAX_PATH> volumePathName; THROW_IF_WIN32_BOOL_FALSE( ::GetVolumePathNameW(path, volumePathName.data(), volumePathName.size())); return volumePathName.data(); } // ボリュームマウントポイントからボリュームのGUIDパスを取得します。 std::wstring GetVolumeGUIDPathFromVolumeMountPoint(LPCWSTR volumeMountPoint) noexcept(false) { THROW_IF_NULL_ALLOC(volumeMountPoint); std::array<WCHAR, MAX_PATH> volumeName; THROW_IF_WIN32_BOOL_FALSE( ::GetVolumeNameForVolumeMountPointW( volumeMountPoint, volumeName.data(), volumeName.size())); return volumeName.data(); } // ファイルパスを含むボリュームのファイルハンドルを取得します。 wil::unique_hfile GetVolumeHandleForPath(LPCWSTR path) noexcept(false) { auto volumePathName = GetVolumePathNameW(path); auto volumeGUIDPath = GetVolumeGUIDPathFromVolumeMountPoint(volumePathName.data()); auto len = volumeGUIDPath.length(); volumeGUIDPath[len - 1] = L'\0'; auto hfile = wil::unique_hfile{ ::CreateFileW( volumeGUIDPath.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr) }; THROW_IF_WIN32_ERROR(::GetLastError()); return hfile; } // パスを含むデバイスがシークにペナルティを持つか確認します。 // ペナルティがない場合、おそらくSSDあるいはRAMドライブです。 // 失敗時は例外を発生します。 bool QueryDeviceIncursSeekPenalty(LPCWSTR path) noexcept(false) { auto hfile = GetVolumeHandleForPath(path); STORAGE_PROPERTY_QUERY query{ StorageDeviceSeekPenaltyProperty, PropertyStandardQuery }; DWORD bytesWritten; DEVICE_SEEK_PENALTY_DESCRIPTOR result; THROW_IF_WIN32_BOOL_FALSE(DeviceIoControl(hfile.get(), IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &result, sizeof(result), &bytesWritten, nullptr)); return result.IncursSeekPenalty != 0; } #include <iostream> int main() { std::cout << "C: " << std::boolalpha << QueryDeviceIncursSeekPenalty(L"C:") << std::endl; // Dドライブが存在しない場合、例外が発生します。 std::cout << "D: " << std::boolalpha << QueryDeviceIncursSeekPenalty(L"D:") << std::endl; return 0; }
補足
std::array<WCHAR, MAX_PATH>
はこの使い方であればWCHAR[MAX_PATH]
とstd::size
の組み合わせの方が簡潔かもしれません。ここではSTLコンテナへの統一のため意図的に使用しています。- いくつかの関数は戻り値として
std::wstring
を返しています。C++17以降は値のコピー省略(狭義のRVO)が保証されるため、戻り値を変数や引数へ代入してもインスタンスは一度だけ作成されます(コピーコンストラクタは呼び出されません)。 THROW_IF_*
はWILのマクロです。特定条件で例外を発生します。- 関数の
noexcept(false)
は省略可能です。省略不可能な(省略するとnoexcept
となる)のはデストラクタ等です。