//************************************************************//
//**************Program odczytuje wszystkie dane o HDD********//
//jakie udostępnia technologia S.M.A.R.T. Można go używac w //
//swoich programach za darmo. ******************************//
//***********************************************************//
//************Pytania i co tam chcecie walcie na:************//
//****************marcin.antosz@interia.pl (gg:1198794)******//
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  smart, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  IdOutCmd: array [1 .. sizeof (SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1] of Byte;
  AttrOutCmd: array [1 .. sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1] of Byte;
  ThreshOutCmd: array [1 ..sizeof(SENDCMDOUTPARAMS) + READ_THRESHOLD_BUFFER_SIZE - 1] of Byte;

// forwards;
function DoIDENTIFY (hSMARTIOCTL: THandle;
                     pSCIP: PSENDCMDINPARAMS;
                     pSCOP: PSENDCMDOUTPARAMS;
                     bIDCmd: Byte;
                     bDriveNum: Byte;
                     var lpcbBytesReturned: DWORD): Boolean; forward;
procedure DisplayIdInfo(pids: PIDSECTOR;
                        pSCIP: PSENDCMDINPARAMS;
                        bIDCmd: Byte;
                        bDfpDriveMap: Byte;
                        bDriveNum: Byte); forward;
procedure PrintIDERegs (pscip: PSENDCMDINPARAMS); forward;
function DoEnableSMART (hSMARTIOCTL: THandle;
                        pSCIP: PSENDCMDINPARAMS;
                        pSCOP: PSENDCMDOUTPARAMS;
                        bDriveNum: Byte;
                        var lpcbBytesReturned: DWORD): Boolean; forward;
function DoReadAttributesCmd (hSMARTIOCTL: THandle;
                              pSCIP: PSENDCMDINPARAMS;
                              pSCOP: PSENDCMDOUTPARAMS;
                              bDriveNum: Byte): Boolean; forward;
function DoReadThresholdsCmd (hSMARTIOCTL: THandle;
                              pSCIP: PSENDCMDINPARAMS;
                              pSCOP: PSENDCMDOUTPARAMS;
                              bDriveNum: Byte): Boolean; forward;
procedure DoPrintData (pAttrBuffer: PCHAR;
                       pThrsBuffer: PCHAR;
                       bDriveNum: Byte); forward;
procedure ChangeByteOrder (szString: PChar; uscStrSize: Word); forward;

procedure TForm1.Button1Click(Sender: TObject);
var
  pHandle: THandle;
  pRet:    DWord;
  pVParam: GETVERSIONOUTPARAMS;
  pSCIP:   SENDCMDINPARAMS;
  pOutCmd: SENDCMDOUTPARAMS;

  i: Integer;
  pIdCmd: Byte;

  bDfpDriveMap: Byte;

  pResult: Boolean;
begin
{  pHandle := CreateFile('\\.\SMARTVSD',  //
                        0,                //
                        0,                //Jeżeli system to Win 9x to musisz
0, CREATE_NEW, 0, 0); }                   //        aktywowac ten kod w komentarzu


  pHandle := CreateFile('\\.\PhysicalDrive0',                //
                         GENERIC_READ  + GENERIC_WRITE,      //
                         FILE_SHARE_READ + FILE_SHARE_WRITE, //Ten kod jest dla Win 2K/NT/XP
                         nil,                                // Jeżeli masz Win 9x to we ten
                         OPEN_EXISTING,                      //      kod w komentarz {} i usuń
                         0,                                  //       znaki komentarza w kodzie
                         0);                                 //           piętro wyżej ;)

  pRet := GetLastError;
  Memo1.Clear;

  if (pHandle = INVALID_HANDLE_VALUE) then
    exit;


  ZeroMemory (@pVParam, sizeof (pVParam));

  if (DeviceIoControl (pHandle, DFP_GET_VERSION, nil, 0,
                       @pVParam,
                       sizeof (pVParam),
                       pRet, nil) ) then
    with Memo1.Lines do begin
      Add ('DFP_GET_VERSION returned:');
      Add (Format ('bVersion        = %d', [pVParam.bVersion]));
      Add (Format ('bRevision       = %d', [pVParam.bRevision]));
      Add (Format ('fCapabilities   = 0x%x', [pVParam.fCapabilities]));
      Add (Format ('bReserved       = 0x%x', [pVParam.bReserved]));
      Add (Format ('bIDEDeviceMap   = 0x%x', [pVParam.bIDEDeviceMap]));
      Add (Format ('cbBytesReturned = %d', [pRet]));
      Add ('');
    end; {with/if}

  bDfpDriveMap := 0;
//
// Now, get the ID sector for all IDE devices in the system.
// If the device is ATAPI use the IDE_ATAPI_ID command,
// otherwise use the IDE_ID_FUNCTION command.
//
  for i := 0 to MAX_IDE_DRIVES - 1 do begin
    if ((pVParam.bIDEDeviceMap shr i) and $10) <> 0 then
      pIDCmd := IDE_ATAPI_ID
    else
      pIDCmd := IDE_ID_FUNCTION;

    ZeroMemory (@pSCIP, sizeof (SENDCMDINPARAMS));
    ZeroMemory (@pOutCmd, sizeof(SENDCMDOUTPARAMS));

	  if (DoEnableSMART (pHandle, @pSCIP, @pOutCmd, i, pRet)) then begin
      Memo1.Lines.Add ('  smart aktywny na dysku ' + IntToStr (i));

      bDfpDriveMap := bDfpDriveMap + (1 shr i);
    end  {if}
    else begin
      Memo1.Lines.Add ('  smart nie aktywny na dysku ' + IntToStr (i));
      Memo1.Lines.Add ('');
      continue;
    end; {else}

    if DoIDENTIFY (pHandle, @pSCIP, PSENDCMDOUTPARAMS(@IdOutCmd), pIDCmd, i, pRet) then begin
      DisplayIdInfo ( (PIDSECTOR ( @ (PSENDCMDOUTPARAMS (@IdOutCmd)^.bBuffer))),
                     @pSCIP, pIDCmd, bDfpDriveMap, i);
    end; {if}

    Memo1.Lines.Add ('');
  end; {for}

//
// Loop through all possible IDE drives and send commands to the ones that
// support SMART.
//
	for i := 0 to MAX_IDE_DRIVES - 1 do begin
    if ((bDfpDriveMap shr i) and 1) = 0 then
      continue;

    ZeroMemory (Pointer (@AttrOutCmd), sizeof (AttrOutCmd));
    ZeroMemory (Pointer (@ThreshOutCmd), sizeof (ThreshOutCmd));

    pResult := DoReadAttributesCmd (pHandle, @pSCIP,
                                    PSENDCMDOUTPARAMS(@AttrOutCmd), i);
    with Memo1.Lines do
      if not pResult then begin
        Add ( Format ('SMART Read Attr Command Failed on Drive: %d.', [i]));
				Add ( Format (' DriverStatus: bDriverError=0x%X, bIDEStatus=0x%X',
					[PSENDCMDOUTPARAMS(Pointer(@AttrOutCmd))^.DriverStatus.bDriverError,
					 PSENDCMDOUTPARAMS(Pointer(@AttrOutCmd))^.DriverStatus.bIDEStatus]));
      end  {if}
      else begin
//
// ReadAttributes worked. Try ReadThresholds.
//
        if not DoReadThresholdsCmd(pHandle, @pSCIP,
                                   PSENDCMDOUTPARAMS(@ThreshOutCmd), i) then begin
          Add (Format ('SMART Read Thrsh Command Failed on Drive: %d.', [i]));
          Add (Format (' DriverStatus: bDriverError=0x%X, bIDEStatus=0x%X',
            [ (PSENDCMDOUTPARAMS(Pointer (@ThreshOutCmd)))^.DriverStatus.bDriverError,
              (PSENDCMDOUTPARAMS(Pointer (@ThreshOutCmd)))^.DriverStatus.bIDEStatus]));
        end; {if}

//
// The following report will print if ReadAttributes works.
// If ReadThresholds works, the report will also show values for
// Threshold values.
//
      if (pResult) then begin
        DoPrintData (
          PChar (@(PSENDCMDOUTPARAMS(Pointer (@AttrOutCmd)))^.bBuffer),
          PChar (@(PSENDCMDOUTPARAMS(Pointer (@ThreshOutCmd)))^.bBuffer), i);
      end; {if}
    end; {else}  
  end; {for}

  CloseHandle (pHandle);
end;

function DoIDENTIFY (hSMARTIOCTL: THandle;
                     pSCIP: PSENDCMDINPARAMS;
                     pSCOP: PSENDCMDOUTPARAMS;
                     bIDCmd: Byte;
                     bDriveNum: Byte;
                     var lpcbBytesReturned: DWORD): Boolean;
begin
//
// Set up data structures for IDENTIFY command.
//
  with pSCIP^ do begin
    cBufferSize := IDENTIFY_BUFFER_SIZE;

    with irDriveRegs do begin
      bFeaturesReg     := 0;
      bSectorCountReg  := 1;
      bSectorNumberReg := 1;
      bCylLowReg       := 0;
      bCylHighReg      := 0;

//
// Compute the drive number.
//
      bDriveHeadReg := $A0 + ((bDriveNum and 1) shl 4);
    end;

//
// The command can either be IDE identify or ATAPI identify.
//
    irDriveRegs.bCommandReg := bIDCmd;
    bDriveNumber            := bDriveNum;
    cBufferSize             := IDENTIFY_BUFFER_SIZE;
  end; // with pSCIP^

{
  function DeviceIoControl(hDevice: THandle; dwIoControlCode: DWORD; lpInBuffer: Pointer;
  nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD;
  var lpBytesReturned: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
}

  Result := DeviceIoControl(hSMARTIOCTL, DWORD (DFP_RECEIVE_DRIVE_DATA),
          Pointer( pSCIP), sizeof(SENDCMDINPARAMS) - 1,
          Pointer( pSCOP), sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1,
          lpcbBytesReturned, nil);
end; {}

(****************************************************************************
*
* DisplayIdInfo
*
* Display the contents of the ID buffer
*
****************************************************************************)
procedure DisplayIdInfo(pids: PIDSECTOR;
                        pSCIP: PSENDCMDINPARAMS;
                        bIDCmd: Byte;
                        bDfpDriveMap: Byte;
                        bDriveNum: Byte);
var
  pOutBuffer: array [0..40] of Byte;
  pStr: String;
begin

  if (bIDCmd = IDE_ID_FUNCTION) then
    with Form1.Memo1.Lines do begin
      Add (Format ('Drive %d is an IDE Hard drive', [bDriveNum]));

      if ((bDfpDriveMap shr bDriveNum) and 1) <> 0 then
        Add ('that supports SMART')
      else
        Add ('');

      Add (Format ('Cylinders: %d, #Heads: %d, #Sectors per Track: %d',
           [pids^.wNumCyls, pids^.wNumHeads, pids^.wSectorsPerTrack]));
        PrintIDERegs(pSCIP);
    end {with / if (bIDCmd = IDE_ID_FUNCTION}
  else
    with Form1.Memo1.Lines do begin
      Add (Format ('Drive %d is an ATAPI device.', [bDriveNum]));
    end; {with / else}


  with Form1.Memo1.Lines do begin
//
// Change the WORD array to a BYTE
// array
//
    ChangeByteOrder (PChar (@pids^.sModelNumber), sizeof (pids^.sModelNumber));

//  memset(szOutBuffer, 0, sizeof(szOutBuffer));
//  strncpy(szOutBuffer, pids->sModelNumber, sizeof(pids->sModelNumber));
    pStr := pids^.sModelNumber;
    Add (Format ('Model number: %s', [pStr]));

//
// Change the WORD array to a BYTE
// array
//
    ChangeByteOrder (PChar (@pids^.sFirmwareRev), sizeof (pids^.sFirmwareRev));

//memset(szOutBuffer,0, sizeof(szOutBuffer));
//strncpy(szOutBuffer, pids->sFirmwareRev, sizeof(pids->sFirmwareRev));
    pStr := pids^.sFirmwareRev;
    Add (Format ('Firmware rev: %s', [pStr]));

//
// Change the WORD array to a BYTE
// array
//
    ChangeByteOrder(PChar (@pids^.sSerialNumber), sizeof (pids^.sSerialNumber));

//memset(szOutBuffer,0, sizeof(szOutBuffer));
//strncpy(szOutBuffer, pids->sSerialNumber, sizeof(pids->sSerialNumber));

    pStr := pids^.sSerialNumber;
    Add (Format ('Serial number: %s', [pStr]));
  end; {wtih}
end;                    

(****************************************************************************
*
* DoPrintData
*
* FUNCTION: Display the SMART Attributes and Thresholds
*
****************************************************************************)

procedure DoPrintData (pAttrBuffer: PCHAR;
                       pThrsBuffer: PCHAR;
                       bDriveNum: Byte);
var
  i: Integer;
  pDA: PDRIVEATTRIBUTE;
  pAT: PATTRTHRESHOLD;
  Attr: Byte;
  pBuf: String;
begin
  with Form1.Memo1.Lines do begin
//
// Print the drive number
//
    Add (Format ('Data for Drive Number %d', [bDriveNum]));

//
// Print the revisions of the data structures
//
    Add ('Attribute Structure Revision          ' +
         'Threshold Structure Revision');
    Add (Format ('             %d                       ' +
         '               %d', [WORD(pAttrBuffer[0]), WORD(pThrsBuffer[0])]));

//
// Print the header and loop through the structures, printing
// the structures when the attribute ID is known.
//
  	Add ('      -Attribute Name-          -Thrh - -Actual- -Worst-');


    pDA := PDRIVEATTRIBUTE(@pAttrBuffer[2]);

    pAT := PATTRTHRESHOLD(@pThrsBuffer[2]);

    for i := 0 to (NUM_ATTRIBUTE_STRUCTS - 1) do begin
//      Attr := tmp1[i].bAttrID;

      Attr := pDA^.bAttrID;

      if Attr = 0 then
        continue;

      pBuf := Format ('%2X - %s ', [pDA^.bAttrID, AttribName (Attr)]);
      pBuf := pBuf + StatFlagsToStars (pDA^.wStatusFlags);
      pBuf := pBuf + Format (' - %3d   - %3d    - %3d - ',
                    [pDA^.bAttrValue,
                    pDA^.bWorstValue,
                    pAT^.bWarrantyThreshold]);
      Add (pBuf);

      pDA := PDRIVEATTRIBUTE (Integer (pDA) + sizeof (DRIVEATTRIBUTE));
      pAT := PATTRTHRESHOLD  (Integer (pDA) + sizeof (ATTRTHRESHOLD));
    end; {for}
  end; {with Form1.Memo1.Lines}
end; {}

//---------------------------------------------------------------------
// Display contents of IDE hardware registers reported by SMART
//---------------------------------------------------------------------
procedure PrintIDERegs (pscip: PSENDCMDINPARAMS);
begin
  with Form1.Memo1.Lines do begin
    Add('IDE TASK FILE REGISTERS');

    Add(Format ('tbFeaturesReg     = 0x%X', [pscip^.irDriveRegs.bFeaturesReg]));
    Add(Format ('tbSectorCountReg  = 0x%X', [pscip^.irDriveRegs.bSectorCountReg]));
    Add(Format ('tbSectorNumberReg = 0x%X', [pscip^.irDriveRegs.bSectorNumberReg]));
    Add(Format ('tbCylLowReg       = 0x%X', [pscip^.irDriveRegs.bCylLowReg]));
    Add(Format ('tbCylHighReg      = 0x%X', [pscip^.irDriveRegs.bCylHighReg]));
    Add(Format ('tbDriveHeadReg    = 0x%X', [pscip^.irDriveRegs.bDriveHeadReg]));
    Add(Format ('tStatus           = 0x%X', [pscip^.irDriveRegs.bCommandReg]));
  end; {with}
end; (*PrintIDERegs*)

(****************************************************************************
*
* DoEnableSMART
*
* FUNCTION: Send a SMART_ENABLE_SMART_OPERATIONS command to the drive
* bDriveNum = 0-3
*
****************************************************************************)
function DoEnableSMART (hSMARTIOCTL: THandle;
                        pSCIP: PSENDCMDINPARAMS;
                        pSCOP: PSENDCMDOUTPARAMS;
                        bDriveNum: Byte;
                        var lpcbBytesReturned: DWORD): Boolean;
begin
//
// Set up data structures for Enable SMART Command.
//
  with pSCIP^ do begin
    cBufferSize := 0;

    with irDriveRegs do begin
      bFeaturesReg     := SMART_ENABLE_SMART_OPERATIONS;
      bSectorCountReg  := 1;
      bSectorNumberReg := 1;
      bCylLowReg       := SMART_CYL_LOW;
      bCylHighReg      := SMART_CYL_HI;
    end; {with irDriveRegs}

//
// Compute the drive number.
//
    irDriveRegs.bDriveHeadReg := $A0 + ((bDriveNum and 1) shl 4);
    irDriveRegs.bCommandReg   := IDE_EXECUTE_SMART_FUNCTION;

    bDriveNumber := bDriveNum;
  end; {with pSCIP}

  Result := DeviceIoControl (hSMARTIOCTL, DFP_SEND_DRIVE_COMMAND,
                Pointer (pSCIP), sizeof(SENDCMDINPARAMS) - 1,
                Pointer (pSCOP), sizeof(SENDCMDOUTPARAMS) - 1,
                lpcbBytesReturned, nil);
end;

(****************************************************************************
*
* DoReadAttributesCmd
*
* FUNCTION: Send a SMART_READ_ATTRIBUTE_VALUES command to the drive
* bDriveNum = 0-3
*
****************************************************************************)
function DoReadAttributesCmd (hSMARTIOCTL: THandle;
                              pSCIP: PSENDCMDINPARAMS;
                              pSCOP: PSENDCMDOUTPARAMS;
                              bDriveNum: Byte): Boolean;
var
 pRet: DWORD;
begin
  with pSCIP^ do begin
//
// Set up data structures for Read Attributes SMART Command.
//
	  cBufferSize := READ_ATTRIBUTE_BUFFER_SIZE;

    with irDriveRegs do begin
      bFeaturesReg     := SMART_READ_ATTRIBUTE_VALUES;
      bSectorCountReg  := 1;
      bSectorNumberReg := 1;
      bCylLowReg       := SMART_CYL_LOW;
      bCylHighReg      := SMART_CYL_HI;
    end; {with irDriveRegs}

//
// Compute the drive number.
//
	  irDriveRegs.bDriveHeadReg := $A0 + ((bDriveNum and 1) shl 4);
	  irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
	  bDriveNumber := bDriveNum;
  end; {with pSCIP^}

  Result := DeviceIoControl(hSMARTIOCTL, DFP_RECEIVE_DRIVE_DATA,
                Pointer (pSCIP), sizeof(SENDCMDINPARAMS) - 1,
                Pointer (pSCOP), sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1,
                pRet, nil);
end;

(****************************************************************************
*
* DoReadThresholdsCmd
*
* FUNCTION: Send a SMART_READ_ATTRIBUTE_THRESHOLDS command to the drive
* bDriveNum = 0-3
*
****************************************************************************)
function DoReadThresholdsCmd (hSMARTIOCTL: THandle;
                              pSCIP: PSENDCMDINPARAMS;
                              pSCOP: PSENDCMDOUTPARAMS;
                              bDriveNum: Byte): Boolean;
var
  pRet: DWORD;
begin
  with pSCIP^ do begin
//
// Set up data structures for Read Thresholds SMART Command.
//

    cBufferSize := READ_THRESHOLD_BUFFER_SIZE;

    with irDriveRegs do begin
      bFeaturesReg     := SMART_READ_ATTRIBUTE_THRESHOLDS;
      bSectorCountReg  := 1;
      bSectorNumberReg := 1;
      bCylLowReg       := SMART_CYL_LOW;
      bCylHighReg      := SMART_CYL_HI;
    end; {with irDriveRegs}

//
// Compute the drive number.
//
    irDriveRegs.bDriveHeadReg := $A0 + ((bDriveNum and 1) shl 4);
    irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
    bDriveNumber := bDriveNum;
  end; {with pSCIP}

  Result := DeviceIoControl(hSMARTIOCTL, DFP_RECEIVE_DRIVE_DATA,
                Pointer (pSCIP), sizeof(SENDCMDINPARAMS) - 1,
                Pointer (pSCOP), sizeof(SENDCMDOUTPARAMS) + READ_THRESHOLD_BUFFER_SIZE - 1,
                pRet, nil);
end;

//---------------------------------------------------------------------
//---------------------------------------------------------------------
procedure ChangeByteOrder (szString: PChar; uscStrSize: Word);
var
  i: Word;
  temp: Char;
begin
	for i := 0 to uscStrSize - 1 do
    if i mod 2 = 0 then begin
      temp := szString[i];
      szString[i] := szString[i+1];
      szString[i+1] := temp;
    end; {}
end;

end.
