DelphiでSMART情報取得|SSD/HDDの健康状態を確認する方法
このページでは、Delphiを使ってSATA接続のSSDやハードディスクのSMART情報(Self-Monitoring Analysis and Reporting Technology)を取得する方法を紹介します。
SATA接続のSMART情報取得またはNVMe接続のHealth Information Logを一括で確認したい方は、Delphiで開発されたWindowsアプリ
SMART/Health Information Log Reader 3.0をお試しください。
NVMe接続のM.2 SSDの健康状態情報をDelphiで取得したい場合は、以下のソースコードをご参照ください:
NVMe接続のM.2 SSDのHealth Information情報を取得するサンプル
SATA接続のM.2 SSD(SATA方式のM.2)に関しては、このページのソースコードでSMART情報の取得が可能です。
ディスクの健康状態や属性値を確認できるユニットを作成し、DeviceIoControl APIを用いた実装例をサンプルコード付きで解説しています。
管理者権限での実行が必要です。
Delphiを起動して新規作成を行う
Delphiを起動し、ファイル→新規作成→Windows VCLアプリケーションをクリックし、
空のアプリケーションを選択してOKボタンを押してプロジェクトを作成します。
TButtonとTMemoをフォームにドラッグ&ドロップします。
すべて保存を押して、プロジェクトフォルダを作成してプロジェクトとユニットを保存します。
SMART情報取得ユニットの作成
ファイル⇒新規作成⇒ユニット-Delphiをクリックして新規ユニットを作成します。
以下ソースを入力してファイル名「USmart.pas」として保存します。
unit USmart;
interface
uses Winapi.Windows, System.SysUtils;
type
TMamSmart=record
AttrID:Byte;
StatusFlags:Word;
Value:Byte;
WorstValue:Byte;
Threshold:Byte;
RawValue:Array[0..5] of Byte;
end;
TMamSmartArray=Array of TMamSmart;
//DriveNum(0~)とMamSmartArrayを与えるとSmart情報をMamSmartArrayに返す
procedure getSmart(DriveNum:Integer;var MamSmartArray:TMamSmartArray);
implementation
const
NUM_ATTR_STRUCTS = 30;
SMART_READ_ATTRIBUTE_VALUES = $D0;
SMART_READ_ATTRIBUTE_THRESHOLDS = $D1;
SMART_CYL_LOW = $4F;
SMART_CYL_HIGH = $C2;
//SMART_CMD Performs SMART cmd.
IDE_EXECUTE_SMART_FUNCTION = $B0;
//BUFFER SIZE
READ_ATTRIBUTE_BUFFER_SIZE = 512;
//DeviceIOControlのAPIへの命令
DFP_GET_VERSION = $00074080;
DFP_SEND_DRIVE_COMMAND = $0007c084;
DFP_RECEIVE_DRIVE_DATA = $0007c088;
type
//Attribute情報の構造体
DriveAttribute = packed record
bAttrID:Byte;
wStatusFlags:Word;
bAttrValue:Byte;
bWorstValue:Byte;
bRawValue:array[0..5] of Byte;
bReserved:Byte;
end;
//DFP_RECEIVE_DRIVE_DATA命令のINとOUTの構造体
_IDEREGS=packed record
bFeaturesReg:Byte;
bSectorCountReg:Byte;
bSectorNumberReg:Byte;
bCylLowReg:Byte;
bCylHighReg:Byte;
bDriveHeadReg:Byte;
bCommandReg:Byte;
bReserved:Byte;
end;
IDEREGS=_IDEREGS;
_SENDCMDINPARAMS=packed record
cBufferSize:DWORD;
irDriveRegs:IDEREGS;
bDriveNumber:Byte;
bReserved:array[0..2] of Byte;
dwReserved:array[0..3] of DWORD;
bBuffer:array[0..0] of Byte;
end;
SENDCMDINPARAMS=_SENDCMDINPARAMS;
_DRIVERSTATUS = packed record
bDriveError:Byte;
bIDEStatus:Byte;
bReserved:array[0..1] of Byte;
dwReserved:array[0..1] of DWORD;
end;
DRIVERSTATUS=_DRIVERSTATUS;
_BSENDCMDOUTPARAMS = packed record
cBufferSize:DWORD;
DriverStatus:DRIVERSTATUS;
bBuffer:array [0..511] of Byte;
end;
BSENDCMDOUTPARAMS=_BSENDCMDOUTPARAMS;
BDriveAttribute = packed record
AttrStructRevision:WORD;
DA:array[0..NUM_ATTR_STRUCTS-1] of DriveAttribute;
end;
PBDriveAttribute =^BDriveAttribute;
//Threshold情報の構造体
AttrThreshold = packed record
bAttrID:Byte;
bWarrantyThreshold:Byte;
bReserved:array[0..9] of Byte;
end;
BAttrThreshold = packed record
ThreStructRevision:Word;
AT:array[0..NUM_ATTR_STRUCTS-1] of AttrThreshold
end;
PBAttrThreshold =^BAttrThreshold;
procedure getSmart(DriveNum:Integer;var MamSmartArray:TMamSmartArray);
var hd:THandle;
st:string;
wst:array[0..127] of char;
br:Cardinal;
SCIP:SENDCMDINPARAMS;
SCOP_ATTR:BSENDCMDOUTPARAMS;
SCOP_THRE:BSENDCMDOUTPARAMS;
PBAttr:PBDriveAttribute;
PBThre:PBAttrThreshold;
i,ct:Integer;
begin
st:='\\.\PhysicalDrive'+Trim(InttoStr(DriveNum));
strpcopy(wst,st);
//deviceハンドルの取得
hd:=CreateFile(wst,GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0);
if hd = INVALID_HANDLE_VALUE then exit;
ZeroMemory(@SCIP,SizeOf(SENDCMDINPARAMS));
ZeroMemory(@SCOP_ATTR,SizeOf(BSENDCMDOUTPARAMS));
SCIP.irDriveRegs.bFeaturesReg:=SMART_READ_ATTRIBUTE_VALUES;
SCIP.irDriveRegs.bSectorCountReg:=1;
SCIP.irDriveRegs.bSectorNumberReg:=1;
SCIP.irDriveRegs.bCylLowReg:=SMART_CYL_LOW;
SCIP.irDriveRegs.bCylHighReg:=SMART_CYL_HIGH;
SCIP.irDriveRegs.bDriveHeadReg:=$A0 or ((DriveNum and 1) shl 4);
SCIP.irDriveRegs.bCommandReg:=IDE_EXECUTE_SMART_FUNCTION;
SCIP.cBufferSize:=READ_ATTRIBUTE_BUFFER_SIZE;
SCIP.bDriveNumber:=DriveNum;
DeviceIoControl(hd,DFP_RECEIVE_DRIVE_DATA,
@SCIP,sizeof(SCIP),@SCOP_ATTR,sizeof(SCOP_ATTR),br,nil);
PBAttr:=PBDriveAttribute(Pointer(@(SCOP_ATTR.bBuffer)[0]));
ZeroMemory(@SCIP,SizeOf(SENDCMDINPARAMS));
ZeroMemory(@SCOP_THRE,SizeOf(BSENDCMDOUTPARAMS));
SCIP.irDriveRegs.bFeaturesReg:=SMART_READ_ATTRIBUTE_THRESHOLDS;
SCIP.irDriveRegs.bSectorCountReg:=1;
SCIP.irDriveRegs.bSectorNumberReg:=1;
SCIP.irDriveRegs.bCylLowReg:=SMART_CYL_LOW;
SCIP.irDriveRegs.bCylHighReg:=SMART_CYL_HIGH;
SCIP.irDriveRegs.bDriveHeadReg:=$A0 or ((DriveNum and 1) shl 4);
SCIP.irDriveRegs.bCommandReg:=IDE_EXECUTE_SMART_FUNCTION;
SCIP.cBufferSize:=READ_ATTRIBUTE_BUFFER_SIZE;
SCIP.bDriveNumber:=DriveNum;
DeviceIoControl(hd,DFP_RECEIVE_DRIVE_DATA,
@SCIP,sizeof(SCIP),@SCOP_THRE,sizeof(SCOP_THRE),br,nil);
PBThre:=PBAttrThreshold(pointer(@SCOP_THRE.bBuffer));
CloseHandle(hd);
setlength(MamSmartArray,0);
ct:=0;
for i := 0 to NUM_ATTR_STRUCTS-1 do
begin
if PBAttr.DA[i].bAttrID>0 then
begin
inc(ct);
setlength(MamSmartArray,ct);
MamSmartArray[ct-1].AttrID:=PBAttr.DA[i].bAttrID;
MamSmartArray[ct-1].StatusFlags:=PBAttr.DA[i].wStatusFlags;
MamSmartArray[ct-1].Value:=PBAttr.DA[i].bAttrValue;
MamSmartArray[ct-1].WorstValue:=PBAttr.DA[i].bWorstValue;
MamSmartArray[ct-1].Threshold:=PBThre.AT[i].bWarrantyThreshold;
MamSmartArray[ct-1].RawValue[0]:=PBAttr.DA[i].bRawValue[0];
MamSmartArray[ct-1].RawValue[1]:=PBAttr.DA[i].bRawValue[1];
MamSmartArray[ct-1].RawValue[2]:=PBAttr.DA[i].bRawValue[2];
MamSmartArray[ct-1].RawValue[3]:=PBAttr.DA[i].bRawValue[3];
MamSmartArray[ct-1].RawValue[4]:=PBAttr.DA[i].bRawValue[4];
MamSmartArray[ct-1].RawValue[5]:=PBAttr.DA[i].bRawValue[5];
end;
end;
end;
end.
Button1クリック時のソースコードを記述する
Unit1のフォームの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 ,USmart;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var reg:TRegistry;
i,j,ct:integer;
smart:TMamSmartArray;
begin
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
//SMART情報の取得
getSmart(i,smart);
//SMART情報が取得出来たら
if length(smart)>0 then
begin
memo1.Lines.Add('DriveNum:'+IntToStr(i));
//属性数ループ
for j := 0 to length(smart)-1 do
begin
memo1.Lines.Add(
format('%3d,%4d,%2d,%2d,%2d,%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x',
[ smart[j].AttrID, smart[j].StatusFlags,
smart[j].Value, smart[j].WorstValue, smart[j].Threshold,
smart[j].RawValue[5], smart[j].RawValue[4],
smart[j].RawValue[3], smart[j].RawValue[2],
smart[j].RawValue[1], smart[j].RawValue[0]
])
);
end;
end;
end;
end;
end.
コンパイルする
[プロジェクト]⇒[すべてのプロジェクトをコンパイル]を選択してコンパイルします。
実行
本アプリケーションは管理者権限が必要(DeviceIoControl APIを使用するには管理者権限が必要となる)なので、
実行ボタンを押して、Button1をクリックしても実行されません。
コンパイルされた実行ファイルを右クリックして「管理者として実行」をクリックして実行し、Button1をクリックします。
