NVMe接続のM.2 SSDのディスクの健康状態の情報を取得 ~Delphiソースコード集
NVMe接続のM.2 SSDのHealth Information情報(ディスクの健康状態の情報)を取得するサンプルソースコードです。
SATA接続のSSD/HDDや、SATA接続のM.2コネクタのSSDからS.M.A.R.T情報(ディスクの健康状態の情報)を取得したい場合は以下URLを参照してください。
SATA接続のSSD,ハードディスクのSMART情報を取得(https://mam-mam.net/delphi/smart.html)
Delphiを管理者権限で起動する
Delphiを管理者権限で起動します

プロジェクトの新規作成と画面設計
ファイル→新規作成→Windows VCLアプリケーションをクリックし、
空のアプリケーションを選択してOKボタンを押してプロジェクトを作成します。
TButtonとTMemoをフォームにドラッグ&ドロップします。
すべて保存を押して、プロジェクトフォルダを作成してプロジェクトとユニットを保存します。

アプリケーションを管理者権限で実行するように設定する
[プロジェクト]⇒[オプション]をクリックします。
左ペインの[アプリケーション]⇒[マニフェスト]をクリックします。
実行レベルを「管理者権限が必要」に設定して「保存」ボタンをクリックします。

ソースコードの記述
「Button1」をダブルクリックしてソースコードを記述します。
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls ,System.Win.Registry, Vcl.Grids; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; NVMeStorageQueryProperty =packed record PropertyId:UInt32; //STORAGE_PROPERTY_ID QueryType:UInt32; //STORAGE_QUERY_TYPE ProtocolType:UInt32; //STORAGE_PROTOCOL_TYPE DataType:UInt32; //STORAGE_PROTOCOL_NVME_DATA_TYPE ProtocolDataRequestValue:UInt32; ProtocolDataRequestSubValue:UInt32; ProtocolDataOffset:UInt32; ProtocolDataLength:UInt32; FixedProtocolReturnData:UInt32; ProtocolDataRequestSubValue2:UInt32; ProtocolDataRequestSubValue3:UInt32; ProtocolDataRequestSubValue4:UInt32; SMARTData:Array[0..511] of Byte; end; TNVMeStorageQueryProperty=NVMeStorageQueryProperty; STORAGE_PROPERTY_QUERY = packed record PropertyId: UInt32; //STORAGE_PROPERTY_ID QueryType: UInt32; //STORAGE_QUERY_TYPE AdditionalParameters: array [0..0] of Byte; end; TStoragePropertyQuery=STORAGE_PROPERTY_QUERY; STORAGE_PROTOCOL_SPECIFIC_DATA=packed record ProtocolType:UInt32;//STORAGE_PROTOCOL_TYPE DataType:UInt32; //STORAGE_PROTOCOL_NVME_DATA_TYPE ProtocolDataRequestValue:UInt32; ProtocolDataRequestSubValue:UInt32; ProtocolDataOffset:UInt32; ProtocolDataLength:UInt32; FixedProtocolReturnData:UInt32; ProtocolDataRequestSubValue2:UInt32; ProtocolDataRequestSubValue3:UInt32; ProtocolDataRequestSubValue4:UInt32; end; TStorageProtocolSpecificData=STORAGE_PROTOCOL_SPECIFIC_DATA; PStorageProtocolSpecificData=^TStorageProtocolSpecificData; NVME_HEALTH_INFO_LOG=packed record DUMMYSTRUCTNAME:Byte; Temperature:Array[0..1] of Byte; AvailableSpare:Byte; AvailableSpareThreshold:Byte; PercentageUsed:Byte; Reserved0:array[0..25] of Byte; DataUnitRead:array[0..15] of Byte; DataUnitWritten:array[0..15] of Byte; HostReadCommands:array[0..15] of Byte; HostWrittenCommands:array[0..15] of Byte; ControllerBusyTime:array[0..15] of Byte; PowerCycle:array[0..15] of Byte; PowerOnHours:array[0..15] of Byte; UnsafeShutdowns:array[0..15] of Byte; MediaErrors:array[0..15] of Byte; ErrorInfoLogEntryCount:array[0..15] of Byte; WarningCompositeTemperatureTime:Cardinal; CriticalCompositeTemperatureTime:Cardinal; TemperatureSensor1:Word; TemperatureSensor2:Word; TemperatureSensor3:Word; TemperatureSensor4:Word; TemperatureSensor5:Word; TemperatureSensor6:Word; TemperatureSensor7:Word; TemperatureSensor8:Word; Reserved1:array[0..295] of Byte; end; TNVMeHealthInfoLog=NVME_HEALTH_INFO_LOG; PNVMeHealthInfoLog=^TNVMeHealthInfoLog; STORAGE_PROPERTY_ID = ( StorageDeviceProperty = 0, StorageAdapterProperty, StorageDeviceIdProperty, StorageDeviceUniqueIdProperty, StorageDeviceWriteCacheProperty, StorageMiniportProperty, StorageAccessAlignmentProperty, StorageDeviceSeekPenaltyProperty, StorageDeviceTrimProperty, StorageDeviceWriteAggregationProperty, StorageDeviceDeviceTelemetryProperty, StorageDeviceLBProvisioningProperty, StorageDevicePowerProperty, StorageDeviceCopyOffloadProperty, StorageDeviceResiliencyProperty, StorageDeviceMediumProductType, StorageAdapterRpmbProperty, StorageAdapterCryptoProperty, StorageDeviceIoCapabilityProperty = 48, StorageAdapterProtocolSpecificProperty, StorageDeviceProtocolSpecificProperty, StorageAdapterTemperatureProperty, StorageDeviceTemperatureProperty, StorageAdapterPhysicalTopologyProperty, StorageDevicePhysicalTopologyProperty, StorageDeviceAttributesProperty, StorageDeviceManagementStatus, StorageAdapterSerialNumberProperty, StorageDeviceLocationProperty, StorageDeviceNumaProperty, StorageDeviceZonedDeviceProperty, StorageDeviceUnsafeShutdownCount, StorageDeviceEnduranceProperty, StorageDeviceLedStateProperty, StorageDeviceSelfEncryptionProperty = 64, StorageFruIdProperty ); STORAGE_QUERY_TYPE = ( PropertyStandardQuery = 0, PropertyExistsQuery, PropertyMaskQuery, PropertyQueryMaxDefined ); STORAGE_PROTOCOL_TYPE=( ProtocolTypeUnknown = $00, ProtocolTypeScsi, ProtocolTypeAta, ProtocolTypeNvme, ProtocolTypeSd, ProtocolTypeUfs, ProtocolTypeProprietary = $7E, ProtocolTypeMaxReserved = $7F ); STORAGE_PROTOCOL_NVME_DATA_TYPE=( NVMeDataTypeUnknown, NVMeDataTypeIdentify, NVMeDataTypeLogPage, NVMeDataTypeFeature ); var Form1: TForm1; const NVME_NAMESPACE_ALL:UINT32 =$FFFFFFFF; NVME_LOG_PAGE_ERROR_INFO = $01; NVME_LOG_PAGE_HEALTH_INFO = $02; NVME_LOG_PAGE_FIRMWARE_SLOT_INFO = $03; NVME_LOG_PAGE_CHANGED_NAMESPACE_LIST = $04; NVME_LOG_PAGE_COMMAND_EFFECTS = $05; NVME_LOG_PAGE_DEVICE_SELF_TEST = $06; NVME_LOG_PAGE_TELEMETRY_HOST_INITIATED = $07; NVME_LOG_PAGE_TELEMETRY_CTLR_INITIATED = $08; NVME_LOG_PAGE_RESERVATION_NOTIFICATION = $80; NVME_LOG_PAGE_SANITIZE_STATUS = $81; NVME_LOG_PAGE_VU_START = $C0; NVME_LOG_PAGE_WCS_CLOUD_SSD = $C0; NVME_LOG_PAGE_VU_END = $FF; implementation {$R *.dfm} function GetNVMeHealthInfoLog(DriveNum:Integer):TNVMeHealthInfoLog; var hd:THandle; st:string; Buffer: array [0..4095] of Byte; //Bufferと同じメモリアドレスを共有する変数を作成 SPQuery:TStoragePropertyQuery absolute Buffer; pSPSpecificData:PStorageProtocolSpecificData; BytesReturned:UInt32; pHealthInfoLog:PNVMeHealthInfoLog; begin ZeroMemory(@result,SizeOf(TNVMeHealthInfoLog)); st:='\\.\PhysicalDrive'+Trim(InttoStr(DriveNum)); //deviceハンドルの取得 hd:=CreateFile(PChar(st),GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 ); if hd<>INVALID_HANDLE_VALUE then begin ZeroMemory(@Buffer[0],Length(Buffer)); SPQuery.PropertyId := Ord(STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty); SPQuery.QueryType := Ord(STORAGE_QUERY_TYPE.PropertyStandardQuery); pSPSpecificData:=@(SPQuery.AdditionalParameters[0]); pSPSpecificData.ProtocolType:=ord(STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme); pSPSpecificData.DataType:=ord(STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage); pSPSpecificData.ProtocolDataRequestValue:=NVME_LOG_PAGE_HEALTH_INFO; pSPSpecificData.ProtocolDataRequestSubValue:=0; pSPSpecificData.ProtocolDataRequestSubValue2:=0; pSPSpecificData.ProtocolDataRequestSubValue3:=0; pSPSpecificData.ProtocolDataRequestSubValue4:=0; pSPSpecificData.ProtocolDataOffset:=SizeOf(STORAGE_PROTOCOL_SPECIFIC_DATA);//40 pSPSpecificData.ProtocolDataLength:=sizeof(NVME_HEALTH_INFO_LOG);//512 pHealthInfoLog:= @buffer[SizeOf(TStoragePropertyQuery)+ Sizeof(TStorageProtocolSpecificData)-1]; if DeviceIoControl(hd,IOCTL_STORAGE_QUERY_PROPERTY, @buffer[0],Length(buffer), @buffer[0],Length(Buffer),BytesReturned,nil) then begin CopyMemory(@result,pHealthInfoLog,SizeOf(TNVMeHealthInfoLog)); end; CloseHandle(hd); end; end; function toRawValue(b:array of Byte):string; var i:Integer; begin result:=''; for i := Low(b) to High(b) do begin result:=result+Format('%2.2x',[b[i]]); end; end; procedure TForm1.Button1Click(Sender: TObject); var reg:TRegistry; i,ct:integer; InfoLog:TNVMeHealthInfoLog; begin Memo1.Clear; ct:=0; //レジストリから物理ドライブ数を取得する reg:=TRegistry.Create(Winapi.Windows.KEY_READ); reg.RootKey:=HKEY_LOCAL_MACHINE; if reg.KeyExists( 'SYSTEM\CurrentControlSet\Services\disk\Enum') then begin reg.OpenKey('SYSTEM\CurrentControlSet\Services\disk\Enum',False); ct:=reg.ReadInteger('Count'); end; reg.CloseKey; reg.Free; for i := 0 to ct-1 do //物理ドライブの数ループ begin InfoLog:=GetNVMeHealthInfoLog(i); if (InfoLog.Temperature[0]<>0) or (InfoLog.Temperature[1]<>0) then begin Memo1.Lines.Add( Format('Temperature:%d℃', [InfoLog.Temperature[0]+(InfoLog.Temperature[1] shl 8)-273] ) +#13#10+ Format('AvailableSpare:%d%%',[InfoLog.AvailableSpare]) +#13#10+ Format('AvailableSpareThreshold:%d%%',[InfoLog.AvailableSpareThreshold]) +#13#10+ Format('PercentageUsed:%d%%',[InfoLog.PercentageUsed]) +#13#10+ Format('DataUnitRead:%s',[toRawValue(InfoLog.DataUnitRead)]) +#13#10+ Format('DataUnitWritten:%s',[toRawValue(InfoLog.DataUnitWritten)]) +#13#10+ Format('HostReadCommands:%s',[toRawValue(InfoLog.HostReadCommands)]) +#13#10+ Format('HostWrittenCommands:%s',[toRawValue(InfoLog.HostWrittenCommands)]) +#13#10+ Format('ControllerBusyTime:%s',[toRawValue(InfoLog.ControllerBusyTime)]) +#13#10+ Format('PowerCycle:%s',[toRawValue(InfoLog.PowerCycle)]) +#13#10+ Format('PowerOnHours:%s',[toRawValue(InfoLog.PowerOnHours)]) +#13#10+ Format('UnsafeShutdowns:%s',[toRawValue(InfoLog.UnsafeShutdowns)]) +#13#10+ Format('MediaErrors:%s',[toRawValue(InfoLog.MediaErrors)]) +#13#10+ Format('ErrorInfoLogEntryCount:%s', [toRawValue(InfoLog.ErrorInfoLogEntryCount)] ) +#13#10+ Format('WarningCompositeTemperatureTime:%d', [InfoLog.WarningCompositeTemperatureTime] ) +#13#10+ Format('CriticalCompositeTemperatureTime:%d', [InfoLog.CriticalCompositeTemperatureTime] ) ); Memo1.Lines.Add( Format('TemperatureSensor1:%d', [InfoLog.TemperatureSensor1]) +#13#10+ Format('TemperatureSensor2:%d', [InfoLog.TemperatureSensor2]) +#13#10+ Format('TemperatureSensor3:%d', [InfoLog.TemperatureSensor3]) +#13#10+ Format('TemperatureSensor4:%d', [InfoLog.TemperatureSensor4]) +#13#10+ Format('TemperatureSensor5:%d', [InfoLog.TemperatureSensor5]) +#13#10+ Format('TemperatureSensor6:%d', [InfoLog.TemperatureSensor6]) +#13#10+ Format('TemperatureSensor7:%d', [InfoLog.TemperatureSensor7]) +#13#10+ Format('TemperatureSensor8:%d', [InfoLog.TemperatureSensor8]) ); end; end; end; end.
実行する
[実行]⇒[実行]をクリック、または「実行」ボタンを押して実行します。
(コンパイルされて実行されます。)
「Button1」をクリックするとNVMe接続のM.2 SSDを搭載していれば、健康状態の情報が表示されます。
