トップへ(mam-mam.net/)

NVMe接続のM.2 SSDのディスクの健康状態の情報を取得 ~Delphiソースコード集

検索:

NVMe接続のM.2 SSDのディスクの健康状態の情報を取得 ~Delphiソースコード集

NVMe接続のM.2 SSDのHealth Information情報(ディスクの健康状態の情報)を取得するサンプルソースコードです。
SATA接続のSSD/HDDから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を搭載していれば、健康状態の情報が表示されます。