有什么方法能获得当前机器的硬盘序列号呢?
各位大虾帮帮忙啊!!!谢谢啦。

解决方案 »

  1.   

    // 读取硬盘序列号    
    unit Unit_Main;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTSrbIoControl = packed recordHeaderLength: ULONG;Signature: array[0..7] of Char;Timeout: ULONG;ControlCode: ULONG;ReturnCode: ULONG;Length: ULONG;end;SRB_IO_CONTROL = TSrbIoControl;PSrbIoControl = ^TSrbIoControl;TIDERegs = packed recordbFeaturesReg: Byte; // Used for specifying SMART "commands".bSectorCountReg: Byte; // IDE sector count registerbSectorNumberReg: Byte; // IDE sector number registerbCylLowReg: Byte; // IDE low order cylinder valuebCylHighReg: Byte; // IDE high order cylinder valuebDriveHeadReg: Byte; // IDE drive/head registerbCommandReg: Byte; // Actual IDE command.bReserved: Byte; // reserved. Must be zero.end;IDEREGS = TIDERegs;PIDERegs = ^TIDERegs;TSendCmdInParams = packed recordcBufferSize: DWORD;irDriveRegs: TIDERegs;bDriveNumber: Byte;bReserved: array[0..2] of Byte;dwReserved: array[0..3] of DWORD;bBuffer: array[0..0] of Byte;end;SENDCMDINPARAMS = TSendCmdInParams;PSendCmdInParams = ^TSendCmdInParams;TIdSector = packed recordwGenConfig: 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: ULONG;wMultSectorStuff: Word;ulTotalAddressableSectors: ULONG;wSingleWordDMA: Word;wMultiWordDMA: Word;bReserved: array[0..127] of Byte;end;PIdSector = ^TIdSector;constIDE_ID_FUNCTION = $EC;IDENTIFY_BUFFER_SIZE = 512;DFP_RECEIVE_DRIVE_DATA = $0007C088;IOCTL_SCSI_MINIPORT = $0004D008;IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501;DataSize = sizeof(TSendCmdInParams) - 1 + IDENTIFY_BUFFER_SIZE;BufferSize = SizeOf(SRB_IO_CONTROL) + DataSize;W9xBufferSize = IDENTIFY_BUFFER_SIZE + 16;typeTForm_Main = class(TForm)Edit_HardDiskNumber: TEdit;Label1: TLabel;Button_Get: TButton;procedure Button_GetClick(Sender: TObject);private{ Private declarations }public{ Public declarations }end;varForm_Main: TForm_Main;implementation{$R *.dfm}function GetIdeDiskSerialNumber: string;typeTSrbIoControl = packed recordHeaderLength: ULONG;Signature: array[0..7] of Char;Timeout: ULONG;ControlCode: ULONG;ReturnCode: ULONG;Length: ULONG;end;SRB_IO_CONTROL = TSrbIoControl;PSrbIoControl = ^TSrbIoControl;TIDERegs = packed recordbFeaturesReg: Byte; // Used for specifying SMART "commands".bSectorCountReg: Byte; // IDE sector count registerbSectorNumberReg: Byte; // IDE sector number registerbCylLowReg: Byte; // IDE low order cylinder valuebCylHighReg: Byte; // IDE high order cylinder valuebDriveHeadReg: Byte; // IDE drive/head registerbCommandReg: Byte; // Actual IDE command.bReserved: Byte; // reserved for future use. Must be zero.end;IDEREGS = TIDERegs;PIDERegs = ^TIDERegs;TSendCmdInParams = packed recordcBufferSize: DWORD; // Buffer size in bytesirDriveRegs: 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;SENDCMDINPARAMS = TSendCmdInParams;PSendCmdInParams = ^TSendCmdInParams;
      

  2.   

    TIdSector = packed recordwGenConfig: 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: ULONG;wMultSectorStuff: Word;ulTotalAddressableSectors: ULONG;wSingleWordDMA: Word;wMultiWordDMA: Word;bReserved: array[0..127] of Byte;end;PIdSector = ^TIdSector;constIDE_ID_FUNCTION = $EC;IDENTIFY_BUFFER_SIZE = 512;DFP_RECEIVE_DRIVE_DATA = $0007C088;IOCTL_SCSI_MINIPORT = $0004D008;IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501;DataSize = sizeof(TSendCmdInParams) - 1 + IDENTIFY_BUFFER_SIZE;BufferSize = SizeOf(SRB_IO_CONTROL) + DataSize;W9xBufferSize = IDENTIFY_BUFFER_SIZE + 16;varhDevice: THandle;cbBytesReturned: DWORD;pInData: PSendCmdInParams;pOutData: Pointer; // PSendCmdInParams;Buffer: array[0..BufferSize - 1] of Byte;srbControl: TSrbIoControl absolute Buffer;procedure ChangeByteOrder(var Data; Size: Integer);var ptr: PChar;i: Integer;c: Char;beginptr := @Data;for i := 0 to (Size shr 1) - 1 dobeginc := ptr^;ptr^ := (ptr + 1)^;(ptr + 1)^ := c;Inc(ptr, 2);end;end;beginResult := '';FillChar(Buffer, BufferSize, #0);if Win32Platform = VER_PLATFORM_WIN32_NT thenbegin // Windows NT, Windows 2000// Get SCSI port handlehDevice := CreateFile('\.:', GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);if hDevice = INVALID_HANDLE_VALUE then Exit;trysrbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);System.Move('SCSIDISK', srbControl.Signature, 8);srbControl.Timeout := 2;srbControl.Length := DataSize;srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;pInData := PSendCmdInParams(PChar(@Buffer) + SizeOf(SRB_IO_CONTROL));pOutData := pInData;with pInData^ dobegincBufferSize := IDENTIFY_BUFFER_SIZE;bDriveNumber := 0;with irDriveRegs dobeginbFeaturesReg := 0;bSectorCountReg := 1;bSectorNumberReg := 1;bCylLowReg := 0;bCylHighReg := 0;bDriveHeadReg := $A0;bCommandReg := IDE_ID_FUNCTION;end;end;if not DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT, @Buffer, BufferSize, @Buffer, BufferSize, cbBytesReturned, nil) then Exit;finallyCloseHandle(hDevice);end;endelsebegin // Windows 95 OSR2, Windows 98hDevice := CreateFile('\.', 0, 0, nil, CREATE_NEW, 0, 0);if hDevice = INVALID_HANDLE_VALUE then Exit;trypInData := PSendCmdInParams(@Buffer);pOutData := PChar(@pInData^.bBuffer);with pInData^ dobegincBufferSize := IDENTIFY_BUFFER_SIZE;bDriveNumber := 0;with irDriveRegs dobeginbFeaturesReg := 0;bSectorCountReg := 1;bSectorNumberReg := 1;bCylLowReg := 0;bCylHighReg := 0;bDriveHeadReg := $A0;bCommandReg := IDE_ID_FUNCTION;end;end;if not DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA, pInData, SizeOf(TSendCmdInParams) - 1, pOutData, W9xBufferSize, cbBytesReturned, nil) then Exit;finallyCloseHandle(hDevice);end;end;with PIdSector(PChar(pOutData) + 16)^ dobeginChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));SetString(Result, sSerialNumber, SizeOf(sSerialNumber));end;end;procedure TForm_Main.Button_GetClick(Sender: TObject);beginEdit_HardDiskNumber.Text := GetIdeDiskSerialNumber;end;end. 
     
       
      

  3.   

    啊,这么复杂啊,难道没有一个API函数可以直接读取序列号吗?
    请问高人以上的代码是从哪里弄来的啊,怎么这么复杂哦?哈哈
      

  4.   

    就下面一个函数:  function GetIdeSerialNumber : pChar;
      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  : 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.
        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
          // 0
          bDriverError : Byte;
          // IDEbDriverError  SMART_IDE_ERROR
          bIDEStatus   : Byte;
          bReserved    : Array[0..1] of Byte;
          dwReserved   : Array[0..1] of DWORD;
        end;
        TSendCmdOutParams = packed record
          // bBuffer
          cBufferSize  : DWORD;
          //
          DriverStatus : TDriverStatus;
          // cBufferSize
          bBuffer      : Array[0..0] of BYTE;
        end;
      var
        hDevice : THandle;
        cbBytesReturned : DWORD;
        //ptr : PVarChar;
        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; //end of ChangeByteOrder
      begin
        Result := ''; //
        if SysUtils.Win32Platform=VER_PLATFORM_WIN32_NT then begin// Windows NT, Windows 2000
          // !  '\\.\PhysicalDrive1\'
          hDevice := CreateFile( '\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
          FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
        end else // Version Windows 95 OSR2, Windows 98
          hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 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;
        //      if Win32Platform=VER_PLATFORM_WIN32_NT then bDriveHeadReg := $A0
        //      else bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4);
                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 := PChar(@sSerialNumber);
        end;
      end;//End of GetIdeSerialNumber
      

  5.   

    用得着这么复杂吗?
        function tform1.GetHDSerialNumber: LongInt;    {$IFDEF WIN32}    var      pdw : pDWord;      mc, fl : dword;    {$ENDIF}    begin      {$IfDef WIN32}      New(pdw);      GetVolumeInformation('c:\',nil,0,pdw,mc,fl,nil,0);      Result := pdw^;      dispose(pdw);     {$ELSE}      Result := GetWinFlags;      {$ENDIF}    end;
      

  6.   

    GetVolumeInformation()返回的是某个分区的序号,我以前用的就是这个函数,我在学校的机房测试了N多次(两天吧),没有一个重复的
      

  7.   

    GetVolumeInformation() 只能取到卷标至于 序列号 嘛呵呵 人家封闭了技术!偶们学不到喽!
      

  8.   

    unit hdsn;interfaceuses
     Windows,SysUtils;function getIDEHardDiskSN: string;
    type
    {*******Use in Win2k ****************}
    TSrbIoControl = packed record
      HeaderLength: ULONG;
      Signature: array[0..7] of Char;
      Timeout: ULONG;
      ControlCode: ULONG;
      ReturnCode: ULONG;
      Length: ULONG;
    end;
    SRB_IO_CONTROL = TSrbIoControl;
    PSrbIoControl = ^TSrbIoControl;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. Must be zero.
    end;
    IDEREGS = TIDERegs;
    PIDERegs = ^TIDERegs;TSendCmdInParams = packed record
      cBufferSize: DWORD;
      irDriveRegs: TIDERegs;
      bDriveNumber: Byte;
      bReserved: array[0..2] of Byte;
      dwReserved: array[0..3] of DWORD;
      bBuffer: array[0..0] of Byte;
    end;
    SENDCMDINPARAMS = TSendCmdInParams;
    PSendCmdInParams = ^TSendCmdInParams;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: ULONG;
      wMultSectorStuff: Word;
      ulTotalAddressableSectors: ULONG;
      wSingleWordDMA: Word;
      wMultiWordDMA: Word;
      bReserved: array[0..127] of Byte;
    end;
    PIdSector = ^TIdSector;{************end Win2k*****************}const
    {********** Use in Win2k **********}
      IDE_ID_FUNCTION = $EC;
      IDENTIFY_BUFFER_SIZE = 512;
      DFP_RECEIVE_DRIVE_DATA = $0007C088;
      IOCTL_SCSI_MINIPORT = $0004D008;
      IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501;
      DataSize = sizeof(TSendCmdInParams) - 1 + IDENTIFY_BUFFER_SIZE;
      BufferSize = SizeOf(SRB_IO_CONTROL) + DataSize;
      W9xBufferSize = IDENTIFY_BUFFER_SIZE + 16;
    {********* End Win2k***************}{********* Use in Win9x ***********}
      hookexceptionno = 5;
    {********* End in Win9x************}var{********* Use in Win9x ***********}
      pw: array[0..255] of Word; // pw[256];
      idtr_1: array[0..5] of Byte; //保存中断描述符表寄存器
      oldexceptionhook: DWord; //保存原先的中断入口地址
      IdeBase: Word;
      SelectDisk: Integer;
      ErrNo: Integer = 0;
    {********* End in Win9x************}implementation{********* Use in Win9x ***********}function inp(rdx: Word): Byte;
    asm
    mov dx, rdx
    in al, dx
    end;function inpw(rdx: Word): Word;
    asm
    mov dx, rdx
    in ax, dx
    end;procedure outp(ral: Byte; rdx: Word);
    asm
    mov dx, rdx
    mov al, ral
    out dx, al
    end;function WaitIde: Byte;
    var
      al: Byte;
    begin
      repeat
        al := inp(IdeBase + 7);
      until (al < $80) or (al = $A0); //$a0可能就没有硬盘
      WaitIde := al;
    end;procedure ReadIDE;
    var
      al: Byte;
      i: Integer;
    begin
      WaitIde;
      outp(SelectDisk, IdeBase + 6);
      al := WaitIde;
      if ((al and $50) <> $50) then
      begin
        ErrNo := 1;
        exit;
      end;
      outp(SelectDisk, IdeBase + 6);
      outp($EC, IdeBase + 7);
      al := WaitIde;
      if ((al and $58) <> $58) then
      begin
        ErrNo := 2;
        exit;
      end;
      for i := 0 to 255 do
      begin
        pw[i] := inpw(IdeBase);
      end;
    end;// 新的中断处理程序procedure ReadIt; assembler;
    asm
    push eax
    push ebx
    push ecx
    push edx
    push esi
    push edi
    // 在这里写读程序
    call ReadIDE
    pop edi
    pop esi
    pop edx
    pop ecx
    pop ebx
    pop eax
    iretd
    end;procedure GetSerialNo; assembler;
    asm
    push eax
    // 获取修改的中断的中断描述符(中断门)地址
    sidt idtr_1
    mov eax,dword ptr idtr_1+02h
    add eax,hookexceptionno*08h+04h
    // 保存原先的中断入口地址
    cli
    push ecx
    mov ecx,dword ptr [eax]
    mov cx,word ptr [eax-04h]
    mov dword ptr oldexceptionhook,ecx
    pop ecx
    // 设置修改的中断入口地址为新的中断处理程序入口地址
    push ebx
    lea ebx,ReadIt
    mov word ptr [eax-04h],bx
    shr ebx,10h
    mov word ptr [eax+02h],bx
    pop ebx
    // 执行中断,转到ring 0(与cih 病毒原理相似!)
    push ebx
    int hookexceptionno
    pop ebx
    // 恢复原先的中断入口地址
    push ecx
    mov ecx,dword ptr oldexceptionhook
    mov word ptr [eax-04h],cx
    shr ecx,10h
    mov word ptr [eax+02h],cx
    pop ecx
    // 结束
    sti
    pop eax
    ret
    end;procedure GetIdeDiskSN9X(DriverNo: integer; var s: string);
    var
      i: Integer;
    begin
    {如果有多硬盘一般可能有4个硬盘,也可能多至8个硬盘}
      ErrNo := 0;
      fillchar(pw, sizeof(pw), 0);
      s := '';{设置基址,
    4,5:IdeBase := $1e8;
    6,7:IdeBase := $168;}
      case DriverNo of
        0, 1: IdeBase := $1F0;
        2, 3: IdeBase := $170;
      end;{ 指定主从,Driver No 是奇数为$B0,偶数为$A0}
      case DriverNo of
        0, 2: SelectDisk := $A0;
        1, 3: SelectDisk := $B0;
      end;  GetSerialNo;
      if ErrNo <> 0 then
        Exit; //读错误
      if (pw[0] = 0) then
        s := ''
      else
        for i := 10 to 20 do
        begin
          s := s + char(pw[i] shr 8) + char(pw[i] and $FF);
        end;
    end;
    {********* End in Win9x************}{*******Use in Win2k ****************}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;
    { Windows NT, Windows 2000
    通过MS的S.M.A.R.T.接口,直接从RING3调用
    API DeviceIoControl()来获取硬盘信息
    Get SCSI port handle}function GetIdeDiskSNNT: string;
    var
      hDevice: THandle;
      cbBytesReturned: DWORD;
      pInData: PSendCmdInParams;
      pOutData: Pointer; // PSendCmdOutParams
      Buffer: array[0..BufferSize - 1] of Byte;
      srbControl: TSrbIoControl absolute Buffer;
    begin
      Result := '';
      FillChar(Buffer, BufferSize, #0);  hDevice := CreateFile('\.Scsi0:', 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
        srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
        System.Move('SCSIDISK', srbControl.Signature, 8);
        srbControl.Timeout := 2;
        srbControl.Length := DataSize;
        srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
        pInData := PSendCmdInParams(PChar(@Buffer) + SizeOf(SRB_IO_CONTROL));
        pOutData := pInData;
        with pInData^ do
        begin
          cBufferSize := IDENTIFY_BUFFER_SIZE;
          bDriveNumber := 0;
          with irDriveRegs do
          begin
            bFeaturesReg := 0;
            bSectorCountReg := 1;
            bSectorNumberReg := 1;
            bCylLowReg := 0;
            bCylHighReg := 0;
            bDriveHeadReg := $A0;
            bCommandReg := IDE_ID_FUNCTION;
          end;
        end;
        if not DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT, @Buffer, BufferSize,
          @Buffer, BufferSize, cbBytesReturned, nil) then
          Exit;
      finally
        CloseHandle(hDevice);
      end;  with PIdSector(PChar(pOutData) + 16)^ do
      begin
        ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
        SetString(Result, sSerialNumber, SizeOf(sSerialNumber));
      end;
      Result := Trim(Result);
    end;
    {********* End in Win2k************}function getIDEHardDiskSN: string;
    begin
      if Win32Platform = VER_PLATFORM_WIN32_NT then
        Result := getIDEDiskSNNT
      else if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
        getIDEDiskSN9x(0, Result)
      else
        Result := '';
    end;end.