Link to home
Start Free TrialLog in
Avatar of Jmaurin
Jmaurin

asked on

Machine unique ID - how to?

Hi...
Please, how can i generate machine unique ID?
I can't use processor serial because some machines don't have it.

I wanna unique ID for a lot of machines on my network (including Pentium (100Mhz), Pentium 2, Athlon, P4, etc etc...)
ASKER CERTIFIED SOLUTION
Avatar of esoftbg
esoftbg
Flag of Bulgaria image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jmaurin
Jmaurin

ASKER

Humm....the problem is: some lan cards enable user to change MAC.
Another problem: MAC is too big...
Another problem: if machine doesnt have lan card, just dial up connection (rare, i'm on a network!) but is possible...

another idea?
I can get HDD serial number under Windows 2000 / XP ...., (can't get it under Win 95 / 98 / Millenium)
esoftbg, you mean about the logical volume HDD ... you have serials for C:, D:, etc...
after format of this logical drives, this numbers are changed...

If you mean physical HDD serial:
https://www.experts-exchange.com/questions/20490497/HDD-physical-serial-number.html

Another tips is to get CPU or BIOS serials, there are such examples/components on the internet, but I don't think you can use it user Windows 2003 Server for example...
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Avatar of Wim ten Brink
Wim ten Brink
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
This code extracts the physical serial number from HDDs:

unit Unit1_Q_21134979;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons, DBT, StdCtrls;

const
      IDE_ID_FUNCTION            = $EC; // Returns ID sector for ATA.
      CAP_IDE_ID_FUNCTION        = 1;   // ATAPI ID command supported
      IDENTIFY_BUFFER_SIZE       = 512;
      DFP_RECEIVE_DRIVE_DATA     = $0007c088;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    ListBox: TListBox;
    procedure SpeedButton1Click(Sender: TObject);
  private   { Private declarations }
  public    { Public declarations }
    function  Physical_SN(Id: Integer): Int64;
  end;

      USHORT = Word;
  TIdBuffer    = array [0..IDENTIFY_BUFFER_SIZE-1] of Byte;
      TGetVersionOutParams = packed record
            bVersion      : BYTE;                 // Binary driver version.
            bRevision     : BYTE;                 // Binary driver revision.
            bReserved     : BYTE;                 // Not used.
            bIDEDeviceMap : BYTE;                 // Bit map of IDE devices.
            fCapabilities : DWORD;                // Bit mask of driver capabilities.
            dwReserved    : Array[0..3] of DWORD; // For future use.
      end;

      TIdSector = packed record
            wGenConfig                 : USHORT;
            wNumCyls                   : USHORT;
            wReserved                  : USHORT;
            wNumHeads                  : USHORT;
            wBytesPerTrack             : USHORT;
            wBytesPerSector            : USHORT;
            wSectorsPerTrack           : USHORT;
            wVendorUnique              : Array[0..2] of USHORT;
            sSerialNumber              : Array[0..19] of CHAR;
            wBufferType                : USHORT;
            wBufferSize                : USHORT;
            wECCSize                   : USHORT;
            sFirmwareRev               : Array[0..7] of CHAR;
            sModelNumber               : Array[0..39] of CHAR;
            wMoreVendorUnique          : USHORT;
            wDoubleWordIO              : USHORT;
            wCapabilities              : USHORT;
            wReserved1                 : USHORT;
            wPIOTiming                 : USHORT;
            wDMATiming                 : USHORT;
            wBS                        : USHORT;
            wNumCurrentCyls            : USHORT;
            wNumCurrentHeads           : USHORT;
            wNumCurrentSectorsPerTrack : USHORT;
            ulCurrentSectorCapacity    : ULONG;
            wMultSectorStuff           : USHORT;
            ulTotalAddressableSectors  : ULONG;
            wSingleWordDMA             : USHORT;
            wMultiWordDMA              : USHORT;
            bReserved                  : Array[0..127] of BYTE;
      end;

      TDriverStatus = packed record
            bDriverError : Byte;                 // Error code from driver, or 0 if no error.
            bIDEStatus   : Byte;                 // Contents of IDE Error register. Only valid when bDriverError is SMART_IDE_ERROR.
            bReserved    : Array[0..1] of Byte;  // Reserved for future expansion.
            dwReserved   : Array[0..1] of DWORD; // Reserved for future expansion.
      end;

      TSendCmdOutParams = packed record
            cBufferSize  : DWORD;               // Size of bBuffer in bytes
            DriverStatus : TDriverStatus;       // Driver status structure.
            bBuffer      : Array[0..0] of BYTE; // Buffer of arbitrary length in which to store the data read from the drive.
      end;

      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
            cBufferSize  : DWORD;                // Buffer size in bytes
            irDriveRegs  : TIDERegs;             // Structure with drive register values.
            bDriveNumber : BYTE;                 // Physical drive number to send command to (0,1,2,3).
            bReserved    : Array[0..2] of Byte;  // Reserved for future expansion.
            dwReserved   : Array[0..3] of DWORD; // For future use.
            bBuffer      : Array[0..0] of Byte;  // Input buffer.
      end;

var
  Form1: TForm1;

var
  aIdBuffer: TIdBuffer;
  Id_Sector: TIdSector absolute aIdBuffer;
function  DirectIdentify(I: DWORD): Boolean;

implementation

{$R *.dfm}

var
  OSVersionInfo: TOSVersionInfo;

function GetPhysicalDriveHandle(DriveNum: Byte; DesireAccess: ACCESS_MASK): THandle;
var
  S:     string;
begin
  OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  GetVersionEx(OSVersionInfo);
  if OSVersionInfo.dwPlatformId=VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
  begin
    Str(DriveNum,s); // avoid SysUtils
    Result := CreateFile( PChar('\\.\PhysicalDrive'+S), DesireAccess, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
  end
  else // Windows 95 OSR2, Windows 98
    Result := CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0 );
  {$ifdef debug}
  if Result=INVALID_HANDLE_VALUE then
    OutputDebugString(PChar('Error on CreateFile: '+SysErrorMessage(GetLastError)));
  {$endif}
end;

function SmartIdentifyDirect( hDevice : THandle; bDriveNum : Byte; bIDCmd : Byte; var IdSector : TIdSector; var IdSectorSize : LongInt ) : BOOL;
const
  BufferSize = SizeOf(TSendCmdOutParams)+IDENTIFY_BUFFER_SIZE-1;
var
  SCIP:        TSendCmdInParams;
             Buffer : Array [0..BufferSize-1] of Byte;
             SCOP : TSendCmdOutParams absolute Buffer;
             dwBytesReturned : DWORD;
begin
  FillChar(SCIP,SizeOf(TSendCmdInParams)-1,#0);
  FillChar(Buffer,BufferSize,#0);
  dwBytesReturned := 0;
  IdSectorSize := 0;
  // Set up data structures for IDENTIFY command.
  with SCIP do
  begin
    cBufferSize  := IDENTIFY_BUFFER_SIZE;
    bDriveNumber := bDriveNum;
    with irDriveRegs do
    begin
      bFeaturesReg     := 0;
      bSectorCountReg  := 1;
      bSectorNumberReg := 1;
      bCylLowReg       := 0;
      bCylHighReg      := 0;
      bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4);
      bCommandReg      := bIDCmd;      // The command can either be IDE identify or ATAPI identify.
    end;
  end;
  Result := DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA, @SCIP, SizeOf(TSendCmdInParams)-1, @SCOP, BufferSize, dwBytesReturned, nil );
  if Result then
  begin
    IdSectorSize := dwBytesReturned-SizeOf(TSendCmdOutParams)+1;
    if IdSectorSize<=0 then IdSectorSize := 0 else System.Move(SCOP.bBuffer,IdSector,IdSectorSize);
  end;
end;

function  DirectIdentify(I: DWORD): Boolean;
var
  B:               Boolean;
  hDevice:         THandle;
  nIdSectorSize:   LongInt;
begin
  B := False;
  FillChar(aIdBuffer, SizeOf(aIdBuffer), #0);
  try
    hDevice := GetPhysicalDriveHandle(I, GENERIC_READ or GENERIC_WRITE);
    try
      {$ifdef debug}
      OutputDebugString(PChar('GetPhysicalDriveHandle return '+IntToHex(hDevice,8)));
      {$endif}
      if (hDevice<>INVALID_HANDLE_VALUE) then
      try
        B := SmartIdentifyDirect(hDevice, 0, IDE_ID_FUNCTION, Id_Sector, nIdSectorSize);
      finally
        {$ifdef debug}
        OutputDebugString('PrintIdSectorInfo end');
        {$endif}
      end;
    finally
      Result := B;
      CloseHandle(hDevice);
    end;
  except
    Result := False;
  end;
end;

function  Hex_To_Dec(S: string): Int64;
var
  C:      Char;
  J:      Integer;
  II:     Int64;
  IH:     Int64;
begin
  IH := 0;
  try
    II := 1;
    while (S<>'') do
    begin
      C := S[Length(S)];
      if (C in ['0'..'9']) then
        J := StrToInt(C)
      else
        J := Ord(C) - 55;
      IH := IH + II * J;
      Delete(S,Length(S),1);
      II := II * 16;
    end;
  finally
    Result := IH;
  end;
end;

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;

function  TForm1.Physical_SN(Id: Integer): Int64;
type
  TacOutBuffer  =  array[0..40] of Char;
var
  I:               Integer;
  IH:              Int64;
  acOutBuffer:     TacOutBuffer;
  T:               string;
begin
  IH := 0;
  try
    DirectIdentify(Id);
    with Id_Sector do
    begin
      ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
      acOutBuffer[SizeOf(sSerialNumber)] := #0;
      StrLCopy(acOutBuffer, sSerialNumber, SizeOf(sSerialNumber));
      I := 0;
      T := '';
      while (acOutBuffer[I] in [#32..#127]) do
      begin
        T := T + acOutBuffer[I];
        Inc(I);
      end;
      T := Trim(T);
      ListBox.Items.Add(Char(Id+67) + ':');
      ListBox.Items.Add('Hex. Serial num:' + T);
      IH := Hex_To_Dec(T);
      ListBox.Items.Add('Dec. Serial num:' + IntToStr(IH));
    end;
  finally
    Result := IH;
  end;
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  ListBox.Clear;
  Physical_SN(0); // C:
  Physical_SN(1); // D:
  Physical_SN(2); // E:
end;

end.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Just another idea.... something I practise...

the serial numbers used for activation of my software are encrypted strings, which contains the customer name. and there is a point in the contract, which doesn't allow them to publish this information and penalty about this...

The other way is to activate your software via web site. Thus you can track when someone wants to active his software with wrong serial or serial, that was alredy used before...
Comment only

Hi

I see that we are once again looking at unique PC identification - often for registering software.

If you are looking at a unique PC ID on the Network - all PC's on a Network (the original question) have a unique ID - its the PC Name!!!!!!

This is 100% true - however the really, really, unique ID under windows is the SID - the Security Identifier.

All PC's have a SID - its what identifies them under Windows.  This is why when you change a PC on a network and give it the same name as the old one, things dont work out - because Windows uses the SID - not the Name.

Voodooman
The SID is still the only unique ID on a Windows network - the rest is rubbish

Voodooman