NVMe SSDの温度情報を取り出す(C++)
SSDの温度情報をコマンドラインで取得したくなったので、ライブラリを調べていてもイマイチ解決しなかったので、いっそ作ってみた。
IOCTL関連とDeviceIoControl関数を利用したHDD温度のよい資料がなかったので、将来わからなくなったときのためにまとめておく。
問題点
NVMe(PCIe)のSSD情報しか取れません。
SATAドライブのSMART情報はWMIコマンドで取得してください。
以下のコードで取得できるはずです。
WMIを使った呼び出し
wmic /namespace:\\Root\WMI path MSStorageDriver_FailurePredictData
Get-WmiObjectを使った呼び出し
Get-WmiObject -Namespace Root\WMI -Class MSStorageDriver_FailurePredictData
VendorSpecificにある194のIDが付いているところに温度情報が入っています。
注意点としては、SATA接続の装置からしか読めません。
(USB不可、NVMe不可)
サポートされていませんと出ます。
##将来検討
SATAとNVMeを識別してどちらも取得できるようにしたい。
###実際のコード
GitHubにアップロードしてみました。
NVMe_GetTemperature
##コード解説
NVMeStorage管理クラスを準備
#pragma once
#include <string>
#include <winnt.h>
using namespace std;
class NVMeStorage
{
private:
LPCWSTR wszDrive;
public:
NVMeStorage();
NVMeStorage(LPCWSTR wszDrive); //ドライブ名を与えるコンストラクタ
int setReadDrive(LPCWSTR wszDrive); //ドライブ名を与える
bool getDriveGeometry(DISK_GEOMETRY* pdg); //ドライブのジオメトリを読み出す
bool getTemperature(STORAGE_TEMPERATURE_DATA_DESCRIPTOR* stdd); //ドライブの温度情報を読み出す
private:
bool getDriveQuery(void* obj, int size); //ドライブ温度情報読み出しから呼ばれるPrivateメソッド
};
bool NVMeStorage::getTemperature(STORAGE_TEMPERATURE_DATA_DESCRIPTOR* stdd)
{
if (stdd == nullptr)
{
return false;
}
BOOL bResult = FALSE; // generic results flag
bResult = getDriveQuery(&(*stdd), sizeof(*stdd));
return bResult;
}
bool NVMeStorage::getDriveQuery(void* obj, int size)
{
HANDLE hDevice = INVALID_HANDLE_VALUE;
BOOL bResult = FALSE;
DWORD junk = 0;
hDevice = CreateFileW(this->wszDrive, // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
if(hDevice == INVALID_HANDLE_VALUE)
{
return false;
}
STORAGE_PROPERTY_QUERY spq;
spq.PropertyId = StorageDeviceTemperatureProperty;
spq.QueryType = PropertyStandardQuery;
bResult = DeviceIoControl(hDevice, // device to be queried
IOCTL_STORAGE_QUERY_PROPERTY, // operation to perform
&spq, sizeof(spq), // no input buffer
obj, size, // output buffer
&junk, // # bytes returned
(LPOVERLAPPED)NULL); // synchronous I/O
CloseHandle(hDevice);
return (bResult);
}
ドライブ読み出しのためのクラスを作成したので、クラスを利用してNVMeの情報を読み出すための方法
#include "NVMeStorage.h"
#define wszDrive L"\\\\.\\PhysicalDrive0"
int wmain(int argc, wchar_t* argv[])
{
auto errcode = 0;
auto Temperature = true;
// 読み出すドライブを設定
NVMeStorage ssd(wszDrive);
// ドライブの温度を読み出し
if(Temperature)
{
STORAGE_TEMPERATURE_DATA_DESCRIPTOR stdd;
bResult = ssd.getTemperature(&stdd);
time_t now = time(NULL);
tm pnow;
localtime_s(&pnow, &now);
if (bResult)
{
wprintf(L"%2d/%2d/%2d %2d:%2d:%2d Temperature = %d\n", pnow.tm_year + 1900, pnow.tm_mon + 1, pnow.tm_mday, pnow.tm_hour, pnow.tm_min, pnow.tm_sec, stdd.TemperatureInfo->Temperature);
}
else
{
wprintf(L"GetDriveQuery failed. Error %ld.\n", GetLastError());
}
errcode &= bResult;
}
return errcode;
##まとめ
目的は簡単なのにIOCTLで検索すると、Linux関連ばかり出てきてWindows関連はほとんど出てこない。そして、出てきたと思ったらQuery投げます。とそこから先に進むのが大変すぎた。
NVMeの温度だけは取れるようになりましたが、CrystalDiskInfoと同じ結果が出てるのできっと正しいはず。
前の方にも記載したけど、ゆくゆくはSATAやUSBの温度も取りたい。
まとまってない…