Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

SATAハードディスクのSMART情報を取得する

~S.M.A.R.T.(Self-Monitoring Analysis and Reporting Technology)


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をクリックします。

Copyright 2019 Mam