首页  编辑  

使用S.M.A.R.T. Ioctl API获取IDE硬盘序列号

Tags: /超级猛料/Hardware.硬件相关/驱动器相关/硬盘/硬盘序列号/   Date Created:

使用S.M.A.R.T. Ioctl API获取IDE硬盘序列号    

(http://www.tommstudio.com)  

--------------------------------------------------------------------------------

通过它你可以获取型号名称, firmware revision,序列号以及其它有关IDE硬盘的信息.

回答:

相关构件:

IdeInfo.zip

许多FAQ中推荐使用GetVolumeInformation来获取硬盘序列号。但是那获取的是卷的序列号,而不是硬盘的序列号。卷 的序列号是在分区格式化时生成或修改。一些公司使用复制工具来为全部新计算机安装软件----通过将一个硬盘复制到 其它硬盘,当然,所有这些计算机上卷的信息(包括序列号)都是相同的。

我建议另外的一个方法:获取真正硬盘的序列号。

不幸的是,下列代码只能工作在IDE硬盘上。

 // Copyright (c) 2000 Alex Konshin

 program IdeSN;

 // PURPOSE: Simple console application that extracts

 //          the first IDE disk serial number.

 {$APPTYPE CONSOLE}

 uses

   Windows,

   SysUtils; // only for Win32Platform and SysErrorMessage

 // -------------------------------------------------------------

 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.  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;

 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; // PSendCmdOutParams

   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, Windows XP

        // Get SCSI port handle

        hDevice := CreateFile('\\.\Scsi0:',

         // Note: '\\.\C:' required administrative privileges.

         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, Windows ME

       hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,

         CREATE_NEW, 0, 0 );

       if hDevice=INVALID_HANDLE_VALUE then Exit;

       try

         pInData := PSendCmdInParams(@Buffer);

         pOutData := @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;

 // =============================================================

 var s : String;

     rc : DWORD;

 begin

   s := GetIdeDiskSerialNumber;

   if s='' then

   begin

     rc := GetLastError;

     if rc=0 then WriteLn('IDE drive does not support SMART feature')

      else WriteLn(SysErrorMessage(rc));

   end

   else WriteLn('Disk serial number: ''', s,'''');

   ReadLn;

 end.

// 更多关于 S.M.A.R.T. ioctl 的信息可查看:

// http://www.microsoft.com/hwdev/download/respec/iocltapi.rtf

// MSDN库中也有一些简单的例子

// Windows Development -> Win32 Device Driver Kit ->

// SAMPLE: SmartApp.exe Accesses SMART stats in IDE drives

// 还可以查看 http://www.mtgroup.ru/~alexk

// IdeInfo.zip - 一个简单的使用了S.M.A.R.T. Ioctl API的Delphi应用程序

// 注意:

// WinNT/Win2000 - 你必须拥有对硬盘的读/写访问权限,可以为Everyone帐户,不需要管理员特权

// Win98

// SMARTVSD.VXD 必须安装到 \windows\system\iosubsys

// (不要忘记在复制后重新启动系统)