Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: How to read the serial no of hard disk?


This question is answered.


Permlink Replies: 7 - Last Post: Feb 1, 2015 8:03 PM Last Post By: George Wei Threads: [ Previous | Next ]
George Wei

Posts: 40
Registered: 9/18/05
How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2015 1:15 PM
Dear all,

I'm trying to read serial no of the hard disk as a unique id of the machine. I tried the code as follows:

class function TDeviceUtils.GetDeviceId: string;
const
  IDENTIFY_BUFFER_SIZE = 512;
type
  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.
  end;
 
  TSendCmdInParams = packed record
    // Buffer size in bytes
    cBufferSize: LongInt;
    // 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.
  end;
 
  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;
  end;
 
  PIdSector = ^TIdSector;
 
  TDriverStatus = packed record
    bDriverError: BYTE;
    bIDEStatus: BYTE;
    bReserved: array [0 .. 1] of BYTE;
    dwReserved: array [0 .. 1] of DWORD;
  end;
 
  TSendCmdOutParams = packed record
    // bBuffer的大小
    cBufferSize: DWORD;
    DriverStatus: TDriverStatus;
    bBuffer: array [0 .. 0] of BYTE;
  end;
var
  hDevice: Thandle;
  cbBytesReturned: DWORD;
  SCIP: TSendCmdInParams;
  aIdOutCmd: array [0 .. (SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1)
    - 1] of BYTE;
  IdOutCmd: TSendCmdOutParams absolute aIdOutCmd;
  procedure ChangeByteOrder(var Data; Size: Integer);
  var
    ptr: pchar;
    i: Integer;
    c: CHAR;
  begin
    ptr := @Data;
    for i := 0 to (Size shr 1) - 1 do
    begin
      c := ptr^;
      ptr^ := (ptr + 1)^;
      (ptr + 1)^ := c;
      Inc(ptr, 2);
    end;
  end;
 
begin
  Result := '';
 
  // '\\.\PhysicalDrive0\ means the first drive, use '\\.\PhysicalDrive1\' for the second one, etc.
  hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if hDevice = INVALID_HANDLE_VALUE then
    Exit;
  try
    FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
    FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
    cbBytesReturned := 0;
    // Set up data structures for IDENTIFY command.
    with SCIP do
    begin
      cBufferSize := IDENTIFY_BUFFER_SIZE;
      // bDriveNumber := 0;
      with irDriveRegs do
      begin
        bSectorCountReg := 1;
        bSectorNumberReg := 1;
        bDriveHeadReg := $A0;
        bCommandReg := $EC;
      end;
    end;
    if not DeviceIoControl(hDevice, $0007C088, @SCIP, SizeOf(TSendCmdInParams) -
      1, @aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then
      Exit;
  finally
    CloseHandle(hDevice);
  end;
  with PIdSector(@IdOutCmd.bBuffer)^ do
  begin
    ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
    (pchar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
    Result := string(sSerialNumber);
  end;
end;


I built the code segment by using Delphi XE 7 and tested it on my Lenovo X230i with Windows 8.1 installed. When it runs to

hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); 


Function CreateFile returns 4294967295 to hDevice, which equals to INVALID_HANDLE_VALUE, it means this code segment fails to get the serail no.

I guess it is due to my hard disk is SATA interfaced, and Windows 8.x force to use GPT (GUID Partition Table) which totally differs with MBR.

Is there a way to read the serial no? Or eventually, how can I identify one computer from another?

Thanks for any clues.

Regards,
George
Eduardo Elias

Posts: 319
Registered: 9/20/12
Re: How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2015 1:38 PM   in response to: George Wei in response to: George Wei
George,

Take a look on this library: http://sourceforge.net/projects/tponguard/

You can use it in your software it works just fine, I am using it.

But if you prefer you can take a look on the source code and get all the
routines already done for what you are asking.

Eduardo

Dear all,

I'm trying to read serial no of the hard disk as a unique id of the
machine. I tried the code as follows:

class function TDeviceUtils.GetDeviceId: string;
const
IDENTIFY_BUFFER_SIZE = 512;
type
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.
end;
TSendCmdInParams = packed record
// Buffer size in bytes
cBufferSize: LongInt;
// 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.
end;
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;
end;
PIdSector = ^TIdSector;
 
TDriverStatus = packed record
bDriverError: BYTE;
bIDEStatus: BYTE;
bReserved: array [0 .. 1] of BYTE;
dwReserved: array [0 .. 1] of DWORD;
end;
TSendCmdOutParams = packed record
// bBuffer???
cBufferSize: DWORD;
DriverStatus: TDriverStatus;
bBuffer: array [0 .. 0] of BYTE;
end;
var
hDevice: Thandle;
cbBytesReturned: DWORD;
SCIP: TSendCmdInParams;
aIdOutCmd: array [0 .. (SizeOf(TSendCmdOutParams) +
IDENTIFY_BUFFER_SIZE - 1)
- 1] of BYTE;
IdOutCmd: TSendCmdOutParams absolute aIdOutCmd;
procedure ChangeByteOrder(var Data; Size: Integer);
var
ptr: pchar;
i: Integer;
c: CHAR;
begin
ptr := @Data;
for i := 0 to (Size shr 1) - 1 do
begin
c := ptr^;
ptr^ := (ptr + 1)^;
(ptr + 1)^ := c;
Inc(ptr, 2);
end;
end;
begin
Result := '';
// '\\.\PhysicalDrive0\ means the first drive, use
'\\.\PhysicalDrive1\' for the second one, etc.
hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or
GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then
Exit;
try
FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
cbBytesReturned := 0;
// Set up data structures for IDENTIFY command.
with SCIP do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
// bDriveNumber := 0;
with irDriveRegs do
begin
bSectorCountReg := 1;
bSectorNumberReg := 1;
bDriveHeadReg := $A0;
bCommandReg := $EC;
end;
end;
if not DeviceIoControl(hDevice, $0007C088, @SCIP,
SizeOf(TSendCmdInParams) -
1, @aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then
Exit;
finally
CloseHandle(hDevice);
end;
with PIdSector(@IdOutCmd.bBuffer)^ do
begin
ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
(pchar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
Result := string(sSerialNumber);
end;
end;


I built the code segment by using Delphi XE 7 and tested it on my
Lenovo X230i with Windows 8.1 installed. When it runs to

hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or
GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

Function CreateFile returns 4294967295 to hDevice, which equals to
INVALID_HANDLE_VALUE, it means this code segment fails to get the
serail no.

I guess it is due to my hard disk is SATA interfaced, and Windows 8.x
force to use GPT (GUID Partition Table) which totally differs with
MBR.

Is there a way to read the serial no? Or eventually, how can I
identify one computer from another?

Thanks for any clues.

Regards,
George
George Wei

Posts: 40
Registered: 9/18/05
Re: How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2015 2:45 AM   in response to: Eduardo Elias in response to: Eduardo Elias
Eduardo,

Actually, I'm working on a project for multi-platforms, so I want a unified registration plan. I just read the readme.txt of OnGuard, it seems it's for Windows only. Am I right? Anyway, I'll spend more time to read the source code.

George

Eduardo Elias wrote:
George,

Take a look on this library: http://sourceforge.net/projects/tponguard/

You can use it in your software it works just fine, I am using it.

But if you prefer you can take a look on the source code and get all the
routines already done for what you are asking.

Eduardo

Dear all,

I'm trying to read serial no of the hard disk as a unique id of the
machine. I tried the code as follows:

class function TDeviceUtils.GetDeviceId: string;
const
IDENTIFY_BUFFER_SIZE = 512;
type
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.
end;
TSendCmdInParams = packed record
// Buffer size in bytes
cBufferSize: LongInt;
// 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.
end;
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;
end;
PIdSector = ^TIdSector;
 
TDriverStatus = packed record
bDriverError: BYTE;
bIDEStatus: BYTE;
bReserved: array [0 .. 1] of BYTE;
dwReserved: array [0 .. 1] of DWORD;
end;
TSendCmdOutParams = packed record
// bBuffer???
cBufferSize: DWORD;
DriverStatus: TDriverStatus;
bBuffer: array [0 .. 0] of BYTE;
end;
var
hDevice: Thandle;
cbBytesReturned: DWORD;
SCIP: TSendCmdInParams;
aIdOutCmd: array [0 .. (SizeOf(TSendCmdOutParams) +
IDENTIFY_BUFFER_SIZE - 1)
- 1] of BYTE;
IdOutCmd: TSendCmdOutParams absolute aIdOutCmd;
procedure ChangeByteOrder(var Data; Size: Integer);
var
ptr: pchar;
i: Integer;
c: CHAR;
begin
ptr := @Data;
for i := 0 to (Size shr 1) - 1 do
begin
c := ptr^;
ptr^ := (ptr + 1)^;
(ptr + 1)^ := c;
Inc(ptr, 2);
end;
end;
begin
Result := '';
// '\\.\PhysicalDrive0\ means the first drive, use
'\\.\PhysicalDrive1\' for the second one, etc.
hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or
GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then
Exit;
try
FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
cbBytesReturned := 0;
// Set up data structures for IDENTIFY command.
with SCIP do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
// bDriveNumber := 0;
with irDriveRegs do
begin
bSectorCountReg := 1;
bSectorNumberReg := 1;
bDriveHeadReg := $A0;
bCommandReg := $EC;
end;
end;
if not DeviceIoControl(hDevice, $0007C088, @SCIP,
SizeOf(TSendCmdInParams) -
1, @aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then
Exit;
finally
CloseHandle(hDevice);
end;
with PIdSector(@IdOutCmd.bBuffer)^ do
begin
ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
(pchar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
Result := string(sSerialNumber);
end;
end;


I built the code segment by using Delphi XE 7 and tested it on my
Lenovo X230i with Windows 8.1 installed. When it runs to

hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or
GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

Function CreateFile returns 4294967295 to hDevice, which equals to
INVALID_HANDLE_VALUE, it means this code segment fails to get the
serail no.

I guess it is due to my hard disk is SATA interfaced, and Windows 8.x
force to use GPT (GUID Partition Table) which totally differs with
MBR.

Is there a way to read the serial no? Or eventually, how can I
identify one computer from another?

Thanks for any clues.

Regards,
George
Eduardo Elias

Posts: 319
Registered: 9/20/12
Re: How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2015 7:40 AM   in response to: George Wei in response to: George Wei
George,

Yes, it is for windows only. It is not perfect but can give you some clues.

Each platform you will need to find a different approach. There is not such
component that works on all platforms that I am aware of. I am in need of
that either.

There is no HD on an android for example. You can use the build Serial number,
but you will find that it is not always reliable.

Cheap chinese androids commonly repeats the same serials. Not sure how it
works on apple platforms.

So it is quite a work to find out.

Eduardo

Eduardo,

Actually, I'm working on a project for multi-platforms, so I want a
unified registration plan. I just read the readme.txt of OnGuard, it
seems it's for Windows only. Am I right? Anyway, I'll spend more time
to read the source code.

George

Eduardo Elias wrote:
George,
Take a look on this library:
http://sourceforge.net/projects/tponguard/

You can use it in your software it works just fine, I am using it.

But if you prefer you can take a look on the source code and get all
the routines already done for what you are asking.

Eduardo

Dear all,

I'm trying to read serial no of the hard disk as a unique id of the
machine. I tried the code as follows:

class function TDeviceUtils.GetDeviceId: string;
const
IDENTIFY_BUFFER_SIZE = 512;
type
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.
end;
TSendCmdInParams = packed record
// Buffer size in bytes
cBufferSize: LongInt;
// 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.
end;
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;
end;
PIdSector = ^TIdSector;
TDriverStatus = packed record
bDriverError: BYTE;
bIDEStatus: BYTE;
bReserved: array [0 .. 1] of BYTE;
dwReserved: array [0 .. 1] of DWORD;
end;
TSendCmdOutParams = packed record
// bBuffer???
cBufferSize: DWORD;
DriverStatus: TDriverStatus;
bBuffer: array [0 .. 0] of BYTE;
end;
var
hDevice: Thandle;
cbBytesReturned: DWORD;
SCIP: TSendCmdInParams;
aIdOutCmd: array [0 .. (SizeOf(TSendCmdOutParams) +
IDENTIFY_BUFFER_SIZE - 1)
- 1] of BYTE;
IdOutCmd: TSendCmdOutParams absolute aIdOutCmd;
procedure ChangeByteOrder(var Data; Size: Integer);
var
ptr: pchar;
i: Integer;
c: CHAR;
begin
ptr := @Data;
for i := 0 to (Size shr 1) - 1 do
begin
c := ptr^;
ptr^ := (ptr + 1)^;
(ptr + 1)^ := c;
Inc(ptr, 2);
end;
end;
begin
Result := '';
// '\\.\PhysicalDrive0\ means the first drive, use
'\\.\PhysicalDrive1\' for the second one, etc.
hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or
GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then
Exit;
try
FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
cbBytesReturned := 0;
// Set up data structures for IDENTIFY command.
with SCIP do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
// bDriveNumber := 0;
with irDriveRegs do
begin
bSectorCountReg := 1;
bSectorNumberReg := 1;
bDriveHeadReg := $A0;
bCommandReg := $EC;
end;
end;
if not DeviceIoControl(hDevice, $0007C088, @SCIP,
SizeOf(TSendCmdInParams) -
1, @aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then
Exit;
finally
CloseHandle(hDevice);
end;
with PIdSector(@IdOutCmd.bBuffer)^ do
begin
ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
(pchar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
Result := string(sSerialNumber);
end;
end;

I built the code segment by using Delphi XE 7 and tested it on my
Lenovo X230i with Windows 8.1 installed. When it runs to

hDevice := CreateFile('\\.\PhysicalDrive0', GENERIC_READ or
GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

Function CreateFile returns 4294967295 to hDevice, which equals to
INVALID_HANDLE_VALUE, it means this code segment fails to get the
serail no.
I guess it is due to my hard disk is SATA interfaced, and Windows
8.x force to use GPT (GUID Partition Table) which totally differs
with MBR.

Is there a way to read the serial no? Or eventually, how can I
identify one computer from another?

Thanks for any clues.

Regards,
George
Douglas Rudd

Posts: 314
Registered: 5/16/97
Re: How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2015 6:31 PM   in response to: George Wei in response to: George Wei
I use this to get hard drive serial number:
uses Winapi.Windows;
 
function GetHDSerialNumber: DWORD;
var
  pdw: pDWord;
  mc, fl: DWORD;
  Drive: string;
begin
  Drive := IncludeTrailingPathDelimiter(ExtractFileDrive(paramstr(0)));
  New(pdw);
  // this gets the volume serial number assigned by OS, not hardware manufacturer
  GetVolumeInformation(PChar(Drive), nil, 0, pdw, mc, fl, nil, 0);
  result := pdw^;
  dispose(pdw);
end;
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 30, 2015 7:03 PM   in response to: Douglas Rudd in response to: Douglas Rudd
Douglas wrote:

I use this to get hard drive serial number:

That is retreiving the volume serial number of the filesystem of the specified
drive. That is not retreiving the serial number of the hard drive itself.

--
Remy Lebeau (TeamB)
Angus Robertson

Posts: 205
Registered: 3/17/00
Re: How to read the serial no of hard disk?
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 31, 2015 9:59 AM   in response to: George Wei in response to: George Wei
I'm trying to read serial no of the hard disk as a unique id of the
machine. I tried the code as follows:

Magenta Systems WMI and SMART Component from

http://www.magsys.co.uk/delphi/magwmi.asp

might help, however the SMART APIs were designed for PCs with only four IDE
ATA drives and drives less than 128 gig, whereas SATA PCs may have six or
more drives. Also they only work with physical drives and not SCSI
connected or RAID drives.

There are Intel RST APIs that access the underlaying drives in PCs using
Intel chipsets which Smartmontools supports, but it's complex C code and
not easy to translate to Delphi.

http://sourceforge.net/projects/smartmontools/

Angus
George Wei

Posts: 40
Registered: 9/18/05
Re: How to read the serial no of hard disk?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 1, 2015 8:03 PM   in response to: Angus Robertson in response to: Angus Robertson
Angus:

WMI function works on my computer, but SMART function does not. The SMART function is very similiar to my code. I haven't tried RST APIs.
I'll test the WMI function on another computer which has a SSD hard disk installed.

Thanks a lot.

George

Angus Robertson wrote:
I'm trying to read serial no of the hard disk as a unique id of the
machine. I tried the code as follows:

Magenta Systems WMI and SMART Component from

http://www.magsys.co.uk/delphi/magwmi.asp

might help, however the SMART APIs were designed for PCs with only four IDE
ATA drives and drives less than 128 gig, whereas SATA PCs may have six or
more drives. Also they only work with physical drives and not SCSI
connected or RAID drives.

There are Intel RST APIs that access the underlaying drives in PCs using
Intel chipsets which Smartmontools supports, but it's complex C code and
not easy to translate to Delphi.

http://sourceforge.net/projects/smartmontools/

Angus
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02