IDE harddisk serial number (Views: 104)


You can get model name, firmware revision, serial number and other IDE harddisk information.


Most FAQ manuals recommend to use GetVolumeInformation for extracting of "harddisk serial number". But it is the volume serial number, not harddisk s/n. It is assigned and changed during formatting of partition. Some companies use cloning tools for installing software on all new computers by copying from the single harddisk to all another. Of course, volume serial numbers for these disks are identical.

Now you can get real serial number of IDE hardisk.
See also my articles:
1204 "IDE harddisk serial number (Part 2)"
1174 "SCSI-2 device serial number"

// Get first IDE harddisk serial number

function GetIdeSerialNumber: SerialNumber;
  TIDERegs = packed record
    bFeaturesReg: BYTE; // Used for specifying SMART "commands".
    bSectorCountReg: BYTE; // IDE sector count register
    bSectorNumberReg: BYTE; // IDE sector number register
    bCylLowReg: BYTE; // IDE low order cylinder value
    bCylHighReg: BYTE; // IDE high order cylinder value
    bDriveHeadReg: BYTE; // IDE drive/head register
    bCommandReg: BYTE; // Actual IDE command.
    bReserved: BYTE; // reserved for future use.  Must be zero.
  TSendCmdInParams = packed record
    // Buffer size in bytes
    cBufferSize: DWORD;
    // Structure with drive register values.
    irDriveRegs: TIDERegs;
    // Physical drive number to send command to (0,1,2,3).
    bDriveNumber: BYTE;
    bReserved: array[0..2] of Byte;
    dwReserved: array[0..3] of DWORD;
    bBuffer: array[0..0] of Byte; // Input buffer.
  TIdSector = packed record
    wGenConfig: Word;
    wNumCyls: Word;
    wReserved: Word;
    wNumHeads: Word;
    wBytesPerTrack: Word;
    wBytesPerSector: Word;
    wSectorsPerTrack: Word;
    wVendorUnique: array[0..2] of Word;
    sSerialNumber: array[0..19] of CHAR;
    wBufferType: Word;
    wBufferSize: Word;
    wECCSize: Word;
    sFirmwareRev: array[0..7] of Char;
    sModelNumber: array[0..39] of Char;
    wMoreVendorUnique: Word;
    wDoubleWordIO: Word;
    wCapabilities: Word;
    wReserved1: Word;
    wPIOTiming: Word;
    wDMATiming: Word;
    wBS: Word;
    wNumCurrentCyls: Word;
    wNumCurrentHeads: Word;
    wNumCurrentSectorsPerTrack: Word;
    ulCurrentSectorCapacity: DWORD;
    wMultSectorStuff: Word;
    ulTotalAddressableSectors: DWORD;
    wSingleWordDMA: Word;
    wMultiWordDMA: Word;
    bReserved: array[0..127] of BYTE;
  PIdSector = ^TIdSector;
  TDriverStatus = packed record
    // Error code from driver, or 0 if no error.
    bDriverError: Byte;
    // Contents of IDE Error register. Only valid when bDriverError is SMART_IDE_ERROR.
    bIDEStatus: Byte;
    bReserved: array[0..1] of Byte;
    dwReserved: array[0..1] of DWORD;
  TSendCmdOutParams = packed record
    // Size of bBuffer in bytes
    cBufferSize: DWORD;
    // Driver status structure.
    DriverStatus: TDriverStatus;
    // Buffer of arbitrary length in which to store the data read from the drive.
    bBuffer: array[0..0] of BYTE;

  hDevice: THandle;
  cbBytesReturned: DWORD;
  ptr: PChar;
  SCIP: TSendCmdInParams;
  aIdOutCmd: array[0..(SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1) - 1] of
  IdOutCmd: TSendCmdOutParams absolute aIdOutCmd;

  procedure ChangeByteOrder(var Data; Size: Integer);
    ptr: PChar;
    i: Integer;
    c: Char;
    ptr := @Data;
    for i := 0 to (Size shr 1) - 1 do
      c := ptr^;
      ptr^ := (ptr + 1)^;
      (ptr + 1)^ := c;
      Inc(ptr, 2);

  Result := ''; // return empty string on error
  if SysUtils.Win32Platform = VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
    // warning! change name for other drives: ex.: second drive '\\.\PhysicalDrive1\'
    hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
  else // Version Windows 95 OSR2, Windows 98
    hDevice := CreateFile('\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0);
  if hDevice = INVALID_HANDLE_VALUE then
    FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
    FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
    cbBytesReturned := 0;
    // Set up data structures for IDENTIFY command.
    with SCIP do
      cBufferSize := IDENTIFY_BUFFER_SIZE;
      //      bDriveNumber := 0;
      with irDriveRegs do
        bSectorCountReg := 1;
        bSectorNumberReg := 1;
        //      if Win32Platform=VER_PLATFORM_WIN32_NT then bDriveHeadReg := $A0
        //      else bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4);
        bDriveHeadReg := $A0;
        bCommandReg := $EC;
    if not DeviceIoControl(hDevice, $0007C088, @SCIP, SizeOf(TSendCmdInParams) - 1,
      @aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then
  with PIdSector(@IdOutCmd.bBuffer)^ do
    ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
    (PChar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
    Result := PChar(@sSerialNumber);

For more information about S.M.A.R.T. IOCTL see http://www.microsoft.com/hwdev/download/respec/iocltapi.rtf

See also sample SmartApp from MSDN Knowledge Base Windows Development -> Win32 Device Driver Kit ->
SAMPLE: SmartApp.exe Accesses SMART stats in IDE drives

see also http://home.earthlink.net/~akonshin/
IdeInfo.zip - sample delphi application using S.M.A.R.T. Ioctl API
IdeInfo2.zip - sample delphi application using S.M.A.R.T. Ioctl API


WinNT/Win2000 - you must have read/WRITE access right to harddisk

SMARTVSD.VXD must be installed in \windows\system\iosubsys
(Do not forget to reboot after copying)

Component Download: http://home.earthlink.net/~akonshin/files/IdeInfo2.zip

<< Back to main page