Solved

Machine unique ID - how to?

Posted on 2004-09-17
14
599 Views
Last Modified: 2010-05-18
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...)
0
Comment
Question by:Jmaurin
  • 3
  • 3
  • 2
  • +3
14 Comments
 
LVL 12

Accepted Solution

by:
esoftbg earned 32 total points
ID: 12084176
Get MAC unique address:

//........

unit Unit_MAC;

interface

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

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    Memo: TMemo;
    procedure SpeedButton1Click(Sender: TObject);
  private{ Private declarations }
  public { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  NB30;


{$R *.dfm}

function GetAdapterInfo(Lana: Char): String;
var
  Adapter: TAdapterStatus;
  NCB: TNCB;
begin
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBRESET);
  NCB.ncb_lana_num := Lana;
  if Netbios(@NCB) <> Char(NRC_GOODRET) then
  begin
    Result := 'MAC not found';
  end;

  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBASTAT);
  NCB.ncb_lana_num := Lana;
  NCB.ncb_callname := '*';

  FillChar(Adapter, SizeOf(Adapter), 0);
  NCB.ncb_buffer := @Adapter;
  NCB.ncb_length := SizeOf(Adapter);
  if Netbios(@NCB) <> Char(NRC_GOODRET) then
  begin
    Result := 'MAC not found';
  end;
  Result := IntToHex(Byte(Adapter.adapter_address[0]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[1]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[2]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[3]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[4]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[5]), 2);
end;

function GetMACAddress: string;
var
  AdapterList: TLanaEnum;
  NCB: TNCB;
begin
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBENUM);
  NCB.ncb_buffer := @AdapterList;
  NCB.ncb_length := SizeOf(AdapterList);
  Netbios(@NCB);
  if (Byte(AdapterList.length) > 0) then
    Result := GetAdapterInfo(AdapterList.lana[0])
  else
    Result := 'MAC not found';
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  Memo.Lines.Add(GetMACAddress);
end;

end.

//........

object Form1: TForm1
  Left = 224
  Top = 128
  BorderIcons = [biSystemMenu, biMinimize]
  BorderStyle = bsSingle
  Caption = 'MAC address'
  ClientHeight = 197
  ClientWidth = 344
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesktopCenter
  PixelsPerInch = 96
  TextHeight = 13
  object SpeedButton1: TSpeedButton
    Left = 8
    Top = 8
    Width = 128
    Height = 22
    Caption = 'Get MAC address'
    OnClick = SpeedButton1Click
  end
  object Memo: TMemo
    Left = 8
    Top = 32
    Width = 256
    Height = 160
    ReadOnly = True
    ScrollBars = ssVertical
    TabOrder = 0
  end
end
0
 

Author Comment

by:Jmaurin
ID: 12084248
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?
0
 
LVL 12

Expert Comment

by:esoftbg
ID: 12084322
I can get HDD serial number under Windows 2000 / XP ...., (can't get it under Win 95 / 98 / Millenium)
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 12084469
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:
http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20490497.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...
0
 
LVL 12

Assisted Solution

by:Ivanov_G
Ivanov_G earned 31 total points
ID: 12084480
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 31 total points
ID: 12084669
You can easily use WMI to retrieve system information through multiple WMI queries. However, people should be allowed to change some hardware, so using serial numbers of harddisks or the MAC address of a network card is a bit useless. Even the BIOS version is unreliable since it can be upgraded. Still, most people just change one or two things at the same time, thus you should check for several things at once, and allow a few mismatches.
Thus, a combination of MAC address, HDD serial number of each harddisk, version of the BIOS, list of other connected devices and whatever else would provide you a good way to identify a machine and allow you to identify it even after some changes to the hardware. (You could consider it the same system as long as 50% is still identical.)

Also, add a weight to everything you use as identification. A serial number would be more important than the number of devices. A MAC address would also be heavyweight factor. The BIOS name more important than the BIOS version.
0
 
LVL 12

Expert Comment

by:esoftbg
ID: 12084718
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.
0
 
LVL 7

Assisted Solution

by:LRHGuy
LRHGuy earned 31 total points
ID: 12085067
Check out Turbopowers' OnGuard package at sourceforge, free and with source:

http://sourceforge.net/projects/tponguard/

It has a routine "CreateMachineID" which should suit your needs. It should be in OnGuard.pas.
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 12085114
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...
0
 
LVL 5

Expert Comment

by:Voodooman
ID: 12087658
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
0
 
LVL 5

Expert Comment

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

Voodooman
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now