我用  IF (trim(GetIdeDiskSerialNumber())<>'') then  s:=GetIdeDiskSerialNumber();
  if (trim(GetIdeSerialNumber())<>'') then s:=GetIdeSerialNumber();
function   GetIdeSerialNumber   : 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   :   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;     
  //   IDE出错寄存器的内容,只有当bDriverError   为   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   :   PChar;     
  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   :=   '';   //   如果出错则返回空串   
  if   SysUtils.Win32Platform=VER_PLATFORM_WIN32_NT   then   //   Windows   NT,   Windows   2000     
  begin     
  //   提示!   改变名称可适用于其它驱动器,如第二个驱动器:   '\\.\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;     附件传不上来,字数有限先传一个
下段传第二个我用这两种方法读硬盘序列号
在XP,2000,2003
PATA和SATA两种接口
下读出序列号都没什么问题
可到客户去,在两台机子上都试了
他们是XP系统,都读不出来

解决方案 »

  1.   

    function GetIdeDiskSerialNumber: string;
    type
      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 for future use.  Must be zero.
      end;
      IDEREGS = TIDERegs;
      PIDERegs = ^TIDERegs;  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;
      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;const
      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;
    var
      hDevice: 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;
      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 := '';
      FillChar(Buffer, BufferSize, #0);
      if Win32Platform = VER_PLATFORM_WIN32_NT then
      begin // Windows NT, Windows 2000
          // Get SCSI port handle
        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;
      end
      else
      begin // Windows 95 OSR2, Windows 98
        hDevice := CreateFile('\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0);
        if hDevice = INVALID_HANDLE_VALUE then Exit;
        try
          pInData := PSendCmdInParams(@Buffer);
          pOutData := PChar(@pInData^.bBuffer);
          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, DFP_RECEIVE_DRIVE_DATA, pInData, SizeOf(TSendCmdInParams) - 1, pOutData, W9xBufferSize, cbBytesReturned, nil) then Exit;
        finally
          CloseHandle(hDevice);
        end;
      end;
      with PIdSector(PChar(pOutData) + 16)^ do
      begin
        ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
        SetString(Result, sSerialNumber, SizeOf(sSerialNumber));
      end;
    end;