NVMe SSDの温度情報を取り出す（C++）

# 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](https://github.com/harunyan/NVMe_GetTemperature)

##コード解説
NVMeStorage管理クラスを準備

```NVMeStorage.h

#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メソッド
};
```


```NVMeStorage.cpp
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の情報を読み出すための方法

```StorageTemperature.cpp
#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の温度も取りたい。

まとまってない…