简体   繁体   English

在Delphi7中,如何检索硬盘唯一的序列号?

[英]in Delphi7, How can I retrieve hard disk unique serial number?

Hi I want to retrieve HDD unique (hardware) serial number. 嗨,我想检索硬盘唯一(硬件)序列号。 I use some functions but in Windows Seven or Vista they don't work correctly because of admin right. 我使用了一些功能,但在Windows 7或Vista中,由于管理员权限,它们无法正常工作。 Is it possible retrieve it without run as Administrator? 是否可以在不以管理员身份运行时检索它?

Following the links in the question comments Sertac posted, I came across this interesting C++ question , where Fredou answered with a nice link to a codeproject example showing how to do this in .NET , which in turn was based on a link to Borland C++ code and article . 根据Sertac发布的问题评论中的链接,我遇到了这个有趣的C ++问题 ,其中Fredou回答了一个很好的链接到一个代码项目示例,展示了如何在.NET中执行此操作,而.NET又基于Borland C ++代码链接文章

The cool thing is that this C++ code works as a non-administrator user too! 很酷的是, 这个C ++代码也可以作为非管理员用户使用!

Now you need someone to help you translate this C++ code to Delphi. 现在你需要有人帮助你将这个C ++代码翻译成Delphi。

Edit : Found a Delphi unit that does this for you. 编辑找到一个Delphi单元,为您完成此任务。

I wrote some sample use for it: 我为它写了一些示例用法:

program DiskDriveSerialConsoleProject;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils,
  hddinfo in 'hddinfo.pas';

const
  // Max number of drives assuming primary/secondary, master/slave topology
  MAX_IDE_DRIVES = 16;

procedure ReadPhysicalDriveInNTWithZeroRights ();
var
  DriveNumber: Byte;
  HDDInfo: THDDInfo;
begin
  HDDInfo := THDDInfo.Create();
  try
    for DriveNumber := 0 to MAX_IDE_DRIVES - 1 do
    try
      HDDInfo.DriveNumber := DriveNumber;
      if HDDInfo.IsInfoAvailable then
      begin
        Writeln('VendorId: ', HDDInfo.VendorId);
        Writeln('ProductId: ', HDDInfo.ProductId);
        Writeln('ProductRevision: ', HDDInfo.ProductRevision);
        Writeln('SerialNumber: ', HDDInfo.SerialNumber);
        Writeln('SerialNumberInt: ', HDDInfo.SerialNumberInt);
        Writeln('SerialNumberText: ', HDDInfo.SerialNumberText);
      end;
    except
      on E: Exception do
        Writeln(Format('DriveNumber %d, %s: %s', [DriveNumber, E.ClassName, E.Message]));
    end;
  finally
    HDDInfo.Free;
  end;
end;

begin
  ReadPhysicalDriveInNTWithZeroRights;
  Write('Press <Enter>');
  Readln;
end.

Unit from http://www.delphipraxis.net/564756-post28.html 单位来自http://www.delphipraxis.net/564756-post28.html

// http://www.delphipraxis.net/564756-post28.html

unit hddinfo;

interface

uses Windows, SysUtils, Classes;

const
  IOCTL_STORAGE_QUERY_PROPERTY = $2D1400;

type
  THDDInfo = class (TObject)
  private
    FDriveNumber: Byte;
    FFileHandle: Cardinal;
    FInfoAvailable: Boolean;
    FProductRevision: string;
    FProductId: string;
    FSerialNumber: string;
    FVendorId: string;
    procedure ReadInfo;
    procedure SetDriveNumber(const Value: Byte);
  public
    constructor Create;
    property DriveNumber: Byte read FDriveNumber write SetDriveNumber;
    property VendorId: string read FVendorId;
    property ProductId: string read FProductId;
    property ProductRevision: string read FProductRevision;
    property SerialNumber: string read FSerialNumber;
    function SerialNumberInt: Cardinal;
    function SerialNumberText: string;
    function IsInfoAvailable: Boolean;
  end;

implementation

type
  STORAGE_PROPERTY_QUERY = packed record
    PropertyId: DWORD;
    QueryType: DWORD;
    AdditionalParameters: array[0..3] of Byte;
  end;

  STORAGE_DEVICE_DESCRIPTOR = packed record
    Version: ULONG;
    Size: ULONG;
    DeviceType: Byte;
    DeviceTypeModifier: Byte;
    RemovableMedia: Boolean;
    CommandQueueing: Boolean;
    VendorIdOffset: ULONG;
    ProductIdOffset: ULONG;
    ProductRevisionOffset: ULONG;
    SerialNumberOffset: ULONG;
    STORAGE_BUS_TYPE: DWORD;
    RawPropertiesLength: ULONG;
    RawDeviceProperties: array[0..511] of Byte;
  end;

function ByteToChar(const B: Byte): Char;
begin
  Result := Chr(B + $30)
end;

function SerialNumberToCardinal (SerNum: String): Cardinal;
begin
  HexToBin(PChar(SerNum), PChar(@Result), SizeOf(Cardinal));
end;

function SerialNumberToString(SerNum: String): String;
var
  I, StrLen: Integer;
  Pair: string;
  B: Byte;
  Ch: Char absolute B;

begin
  Result := '';
  StrLen := Length(SerNum);

  if Odd(StrLen) then Exit;

  I := 1;

  while I < StrLen do
  begin
    Pair := Copy (SerNum, I, 2);
    HexToBin(PChar(Pair), PChar(@B), 1);
    Result := Result + Chr(B);
    Inc(I, 2);
  end;

  I := 1;

  while I < Length(Result) do
  begin
    Ch := Result[I];
    Result[I] := Result[I + 1];
    Result[I + 1] := Ch;
    Inc(I, 2);
  end;
end;

constructor THddInfo.Create;
begin
  inherited;

  SetDriveNumber(0);
end;

function THDDInfo.IsInfoAvailable: Boolean;
begin
  Result := FInfoAvailable
end;

procedure THDDInfo.ReadInfo;
type
  PCharArray = ^TCharArray;
  TCharArray = array[0..32767] of Char;

var
  Returned: Cardinal;
  Status: LongBool;
  PropQuery: STORAGE_PROPERTY_QUERY;
  DeviceDescriptor: STORAGE_DEVICE_DESCRIPTOR;
  PCh: PChar;

begin
  FInfoAvailable := False;
  FProductRevision := '';
  FProductId := '';
  FSerialNumber := '';
  FVendorId := '';

  try
    FFileHandle := CreateFile(
                     PChar('\\.\PhysicalDrive' + ByteToChar(FDriveNumber)),
                     0,
                     FILE_SHARE_READ or FILE_SHARE_WRITE,
                     nil,
                     OPEN_EXISTING,
                     0,
                     0
                   );

    if FFileHandle = INVALID_HANDLE_VALUE then
      RaiseLastOSError;

    ZeroMemory(@PropQuery, SizeOf(PropQuery));
    ZeroMemory(@DeviceDescriptor, SizeOf(DeviceDescriptor));

    DeviceDescriptor.Size := SizeOf(DeviceDescriptor);

    Status := DeviceIoControl(
                FFileHandle,
                IOCTL_STORAGE_QUERY_PROPERTY,
                @PropQuery,
                SizeOf(PropQuery),
                @DeviceDescriptor,
                DeviceDescriptor.Size,
                Returned,
                nil
              );

    if not Status then
      RaiseLastOSError;

    if DeviceDescriptor.VendorIdOffset <> 0 then
    begin
      PCh := @PCharArray(@DeviceDescriptor)^[DeviceDescriptor.VendorIdOffset];
      FVendorId := PCh;
    end;

    if DeviceDescriptor.ProductIdOffset <> 0 then
    begin
      PCh := @PCharArray(@DeviceDescriptor)^[DeviceDescriptor.ProductIdOffset];
      FProductId := PCh;
    end;

    if DeviceDescriptor.ProductRevisionOffset <> 0 then
    begin
      PCh := @PCharArray(@DeviceDescriptor)^[DeviceDescriptor.ProductRevisionOffset];
      FProductRevision := PCh;
    end;

    if DeviceDescriptor.SerialNumberOffset <> 0 then
    begin
      PCh := @PCharArray(@DeviceDescriptor)^[DeviceDescriptor.SerialNumberOffset];
      FSerialNumber := PCh;
    end;

    FInfoAvailable := True;
  finally
    if FFileHandle <> INVALID_HANDLE_VALUE then
      CloseHandle(FFileHandle);
  end;
end;

function THDDInfo.SerialNumberInt: Cardinal;
begin
  Result := 0;
  if ((IsInfoAvailable = True) and (FSerialNumber <> '')) then Result := SerialNumberToCardinal(FSerialNumber)
end;

function THDDInfo.SerialNumberText: string;
begin
  Result := '';
  if ((IsInfoAvailable = True) and (FSerialNumber <> '')) then Result := SerialNumberToString(FSerialNumber)
end;

procedure THDDInfo.SetDriveNumber(const Value: Byte);
begin
  FDriveNumber := Value;
  ReadInfo;
end;

end.

Edit : RAID configurations require special provisions. 编辑RAID配置需要特殊规定。

For instance, I got a RAID system with multiple RAID 5 array; 例如,我有一个带有多个RAID 5阵列的RAID系统; only the first one displays, and it does not show the drive serial numbers, but the serial number of the RAID array: 只显示第一个,并且它不显示驱动器序列号,而是显示RAID阵列的序列号:

VendorId: AMCC
ProductId: 9550SXU-16ML
ProductRevision: 3.08
SerialNumber: 006508296D6A2A00DE82
SerialNumberInt: 688416000

--jeroen --jeroen

You can use the WMI (Windows Management Instrumentation) to get information related to windows hardware. 您可以使用WMI (Windows Management Instrumentation)获取与Windows硬件相关的信息。

Exist two wmi classes wich exposes a property called SerialNumber which store the Number allocated by the manufacturer to identify the physical media. 存在两个wmi类,它们暴露了一个名为SerialNumber的属性,该属性存储the Number allocated by the manufacturer to identify the physical media. these classes are Win32_DiskDrive and Win32_PhysicalMedia .to access the SerialNumber property of these classes you must know the DeviceId of the Disk which is something like this \\\\.\\PHYSICALDRIVE0 . 这些类是Win32_DiskDriveWin32_PhysicalMedia 。要访问这些类的SerialNumber属性,您必须知道Disk的DeviceId ,类似于\\\\.\\PHYSICALDRIVE0 Another way is use a association class which link the Physical drive with the logical drive (C,D,E) 另一种方法是使用一个关联类,它将物理驱动器与逻辑驱动器(C,D,E)链接起来

so you must find this link previous to obtain the serial number. 所以你必须先找到此链接才能获得序列号。 the sequence to find this association is like this. 找到这种关联的顺序是这样的。

Win32_DiskPartition -> Win32_LogicalDiskToPartition -> Win32_DiskDrive Win32_DiskPartition - > Win32_LogicalDiskToPartition - > Win32_DiskDrive

Note 1 : The SerialNumber property for the Win32_DiskDrive class does not exist in Windows Server 2003, Windows XP, Windows 2000, and Windows NT 4.0, so how you are talking about use Windows Vista or Windows 7, will work ok for you. 注意1 :Windows Server 2003,Windows XP,Windows 2000和Windows NT 4.0中不存在Win32_DiskDrive类的SerialNumber属性,因此您如何谈论使用Windows Vista或Windows 7,将对您有效。

Note 2 : The code does not require a administrator account to run. 注意2 :代码不需要管理员帐户运行。

check this code 检查此代码

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

function GetDiskSerial(const Drive:AnsiChar):string;
var
  FSWbemLocator  : OLEVariant;
  objWMIService  : OLEVariant;
  colDiskDrives  : OLEVariant;
  colLogicalDisks: OLEVariant;
  colPartitions  : OLEVariant;
  objDiskDrive   : OLEVariant;
  objPartition   : OLEVariant;
  objLogicalDisk : OLEVariant;
  oEnumDiskDrive : IEnumvariant;
  oEnumPartition : IEnumvariant;
  oEnumLogical   : IEnumvariant;
  iValue         : LongWord;
  DeviceID       : string;
begin;
  Result:='';
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', ''); //Connect to the WMI
  colDiskDrives := objWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive','WQL',0);
  oEnumDiskDrive:= IUnknown(colDiskDrives._NewEnum) as IEnumVariant;
  while oEnumDiskDrive.Next(1, objDiskDrive, iValue) = 0 do
  begin
   DeviceID        := StringReplace(objDiskDrive.DeviceID,'\','\\',[rfReplaceAll]); //Escape the `\` chars in the DeviceID value because the '\' is a reserved character in WMI.
   colPartitions   := objWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskDrive.DeviceID="%s"} WHERE AssocClass = Win32_DiskDriveToDiskPartition',[DeviceID]));//link the Win32_DiskDrive class with the Win32_DiskDriveToDiskPartition class
   oEnumPartition  := IUnknown(colPartitions._NewEnum) as IEnumVariant;
    while oEnumPartition.Next(1, objPartition, iValue) = 0 do
     begin
        colLogicalDisks := objWMIService.ExecQuery('ASSOCIATORS OF {Win32_DiskPartition.DeviceID="'+objPartition.DeviceID+'"} WHERE AssocClass = Win32_LogicalDiskToPartition'); //link the Win32_DiskPartition class with theWin32_LogicalDiskToPartition class.
        oEnumLogical  := IUnknown(colLogicalDisks._NewEnum) as IEnumVariant;
          while oEnumLogical.Next(1, objLogicalDisk, iValue) = 0 do
          begin
            if objLogicalDisk.DeviceID=(Drive+':')  then //compare the device id
            begin
                Result:=objDiskDrive.SerialNumber;
                Exit;
            end;
           objLogicalDisk:=Unassigned;
          end;
        objPartition:=Unassigned;
     end;
  end;
end;


begin
 try
    CoInitialize(nil);
    try
      Writeln(GetDiskSerial('C'));
      Readln;
    finally
    CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

I found this code, it is fixed one and working fine with me on windows 7 64 我找到了这个代码,它是固定的,在Windows 7 64上和我一起正常工作

https://code.google.com/p/dvsrc/downloads/detail?name=20120116DiskId32Port_fixed.7z&can=2&q= https://code.google.com/p/dvsrc/downloads/detail?name=20120116DiskId32Port_fixed.7z&can=2&q=

and this his all work 这是他的全部工作

https://code.google.com/p/dvsrc/downloads/list https://code.google.com/p/dvsrc/downloads/list

Here is another DiskId32 translation from C++ to Delphi by Victor Derevyanko 这是Victor Derevyanko从C ++到Delphi的另一个DiskId32翻译

project: http://code.google.com/p/dvsrc/ 项目: http//code.google.com/p/dvsrc/

Because the first method (WithZeroRights) doesn't work for me, I wrote another for ReadIdeDriveAsScsiDriveInNT method: 因为第一种方法(WithZeroRights)对我不起作用,我为ReadIdeDriveAsScsiDriveInNT方法编写了另一种方法:

unit HDScsiInfo;

interface

uses
  Windows, SysUtils;

const
  IDENTIFY_BUFFER_SIZE = 512;
  FILE_DEVICE_SCSI = $0000001b;
  IOCTL_SCSI_MINIPORT_IDENTIFY = ((FILE_DEVICE_SCSI shl 16) + $0501);
  IDE_ATA_IDENTIFY = $EC;  //  Returns ID sector for ATA.
  IOCTL_SCSI_MINIPORT = $0004D008;  //  see NTDDSCSI.H for definition

type
  TDiskData = array [0..256-1] of DWORD;
  TDriveInfo = record
    ControllerType: Integer; //0 - primary, 1 - secondary, 2 - Tertiary, 3 - Quaternary
    DriveMS: Integer; //0 - master, 1 - slave
    DriveModelNumber: String;
    DriveSerialNumber: String;
    DriveControllerRevisionNumber: String;
    ControllerBufferSizeOnDrive: Int64;
    DriveType: String; //fixed or removable or unknown
    DriveSizeBytes: Int64;
  end;

  THDScsiInfo = class (TObject)
  private
    FDriveNumber: Byte;
    FFileHandle: Cardinal;
    FInfoAvailable: Boolean;
    FProductRevision: string;
    FSerialNumber: string;
    FControllerType: Integer;
    FDriveMS: Integer;
    FDriveModelNumber: string;
    FControllerBufferSizeOnDrive: Int64;
    FDriveType: string;
    FDriveSizeBytes: Int64;
    procedure ReadInfo;
    procedure SetDriveNumber(const Value: Byte);
    procedure PrintIdeInfo(DiskData: TDiskData);
  public
    constructor Create;
    property DriveNumber: Byte read FDriveNumber write SetDriveNumber;
    property ProductRevision: string read FProductRevision;
    property SerialNumber: string read FSerialNumber;
    property ControllerType: Integer read FControllerType;
    property DriveMS: Integer read FDriveMS;
    property DriveModelNumber: string read FDriveModelNumber;
    property ControllerBufferSizeOnDrive: Int64 read FControllerBufferSizeOnDrive;
    property DriveType: string read FDriveType;
    property DriveSizeBytes: Int64 read FDriveSizeBytes;
    function IsInfoAvailable: Boolean;
  end;

implementation

type

  SRB_IO_CONTROL = record
   HeaderLength: Cardinal;
   Signature: array [0..8-1] of Byte;
   Timeout: Cardinal;
   ControlCode: Cardinal;
   ReturnCode: Cardinal;
   Length: Cardinal;
  end;
  PSRB_IO_CONTROL = ^SRB_IO_CONTROL;

  DRIVERSTATUS = 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 Longword;//  Reserved for future expansion.
  end;

  SENDCMDOUTPARAMS = record
   cBufferSize: Longword;//  Size of bBuffer in bytes
   DriverStatus: DRIVERSTATUS;//  Driver status structure.
   bBuffer: array [0..0] of Byte;//  Buffer of arbitrary length in which to store the data read from the                                                       // drive.
  end;
  IDEREGS = 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;
  SENDCMDINPARAMS = record
   cBufferSize: Longword;//  Buffer size in bytes
   irDriveRegs: IDEREGS;   //  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 Longword;//  For future use.
   bBuffer: array [0..0] of Byte;//  Input buffer.     //!TODO: this is array of single element
  end;
  PSENDCMDINPARAMS = ^SENDCMDINPARAMS;

  PSENDCMDOUTPARAMS = ^SENDCMDOUTPARAMS;
  IDSECTOR = record
   wGenConfig: Word;
   wNumCyls: Word;
   wReserved: Word;
   wNumHeads: Word;
   wBytesPerTrack: Word;
   wBytesPerSector: Word;
   wSectorsPerTrack: Word;
   wVendorUnique: array [0..3-1] of Word;
   sSerialNumber: array [0..20-1] of AnsiChar;
   wBufferType: Word;
   wBufferSize: Word;
   wECCSize: Word;
   sFirmwareRev: array [0..8-1] of AnsiChar;
   sModelNumber: array [0..40-1] of AnsiChar;
   wMoreVendorUnique: Word;
   wDoubleWordIO: Word;
   wCapabilities: Word;
   wReserved1: Word;
   wPIOTiming: Word;
   wDMATiming: Word;
   wBS: Word;
   wNumCurrentCyls: Word;
   wNumCurrentHeads: Word;
   wNumCurrentSectorsPerTrack: Word;
   ulCurrentSectorCapacity: Cardinal;
   wMultSectorStuff: Word;
   ulTotalAddressableSectors: Cardinal;
   wSingleWordDMA: Word;
   wMultiWordDMA: Word;
   bReserved: array [0..128-1] of Byte;
  end;
  PIDSECTOR = ^IDSECTOR;

  TArrayDriveInfo = array of TDriveInfo;

type
  DeviceQuery = record
   HeaderLength: Cardinal;
   Signature: array [0..8-1] of Byte;
   Timeout: Cardinal;
   ControlCode: Cardinal;
   ReturnCode: Cardinal;
   Length: Cardinal;
   cBufferSize: Longword;//  Buffer size in bytes
   irDriveRegs: IDEREGS;   //  Structure with drive register values.
   bDriveNumber: Byte;//  Physical drive number to send
   bReserved: array[0..2] of Byte;//  Reserved for future expansion.
   dwReserved: array [0..3] of Longword;//  For future use.
   bBuffer: array [0..0] of Byte;//  Input buffer.     //!TODO: this is array of single element
  end;

function ConvertToString (diskdata: TDiskData;
               firstIndex: Integer;
               lastIndex: Integer;
               buf: PAnsiChar): PAnsiChar;
var
   index: Integer;
   position: Integer;
begin
   position := 0;
      //  each integer has two characters stored in it backwards
   for index := firstIndex to lastIndex do begin
         //  get high byte for 1st character
      buf[position] := AnsiChar(Chr(diskdata [index] div 256));
      inc(position);
         //  get low byte for 2nd character
      buf [position] := AnsiChar(Chr(diskdata [index] mod 256));
      inc(position);
   end;
      //  end the string
   buf[position] := Chr(0);

      //  cut off the trailing blanks
   index := position - 1;
   while (index >0) do begin
//      if not IsSpace(AnsiChar(buf[index]))
      if (AnsiChar(buf[index]) <> ' ')
        then break;
      buf [index] := Chr(0);
      dec(index);
   end;

   Result := buf;
end;

constructor THDScsiInfo.Create;
begin
  inherited;
  SetDriveNumber(0);
end;

function THDScsiInfo.IsInfoAvailable: Boolean;
begin
  Result := FInfoAvailable
end;

procedure THDScsiInfo.PrintIdeInfo (DiskData: TDiskData);
var
  nSectors: Int64;
  serialNumber: array [0..1024-1] of AnsiChar;
  modelNumber: array [0..1024-1] of AnsiChar;
  revisionNumber: array [0..1024-1] of AnsiChar;
begin
      //  copy the hard drive serial number to the buffer
   ConvertToString (DiskData, 10, 19, @serialNumber);
   ConvertToString (DiskData, 27, 46, @modelNumber);
   ConvertToString (DiskData, 23, 26, @revisionNumber);
   FControllerType := FDriveNumber div 2;
   FDriveMS := FDriveNumber mod 2;
   FDriveModelNumber := modelNumber;
   FSerialNumber := serialNumber;
   FProductRevision := revisionNumber;
   FControllerBufferSizeOnDrive := DiskData [21] * 512;
   if ((DiskData [0] and $0080) <> 0)
      then FDriveType := 'Removable'
      else if ((DiskData [0] and $0040) <> 0)
          then FDriveType := 'Fixed'
          else FDriveType := 'Unknown';
//  calculate size based on 28 bit or 48 bit addressing
//  48 bit addressing is reflected by bit 10 of word 83
  if ((DiskData[83] and $400) <> 0) then begin
      nSectors := DiskData[103] * Int64(65536) * Int64(65536) * Int64(65536) +
                    DiskData[102] * Int64(65536) * Int64(65536) +
                    DiskData[101] * Int64(65536) +
                    DiskData[100];
  end else begin
        nSectors := DiskData [61] * 65536 + DiskData [60];
  end;
//  there are 512 bytes in a sector
  FDriveSizeBytes := nSectors * 512;
end;

procedure THDScsiInfo.ReadInfo;
type
  DataArry = array [0..256-1] of WORD;
  PDataArray = ^DataArry;
const
  SENDIDLENGTH = sizeof (SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE;
var
  I: Integer;
  buffer: array [0..sizeof (SRB_IO_CONTROL) + SENDIDLENGTH - 1] of AnsiChar;
  dQuery: DeviceQuery;
  dummy: DWORD;
  pOut: PSENDCMDOUTPARAMS;
  pId: PIDSECTOR;
  DiskData: TDiskData;
  pIdSectorPtr: PWord;
begin
  FInfoAvailable := False;
  FFileHandle := CreateFile (PChar(Format('\\.\Scsi%d:', [FDriveNumber])),
                             GENERIC_READ or GENERIC_WRITE,
                             FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
                             OPEN_EXISTING, 0, 0);
  if (FFileHandle <> INVALID_HANDLE_VALUE) then begin
        ZeroMemory(@dQuery, SizeOf(dQuery));
        dQuery.HeaderLength := sizeof (SRB_IO_CONTROL);
        dQuery.Timeout := 10000;
        dQuery.Length := SENDIDLENGTH;
        dQuery.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
        StrLCopy(@dQuery.Signature, 'SCSIDISK', 8);
        dQuery.irDriveRegs.bCommandReg := IDE_ATA_IDENTIFY;
        dQuery.bDriveNumber := FDriveNumber;
        if (DeviceIoControl (FFileHandle, IOCTL_SCSI_MINIPORT,
                             @dQuery,
                             SizeOf(dQuery),
                             @buffer,
                             sizeof (SRB_IO_CONTROL) + SENDIDLENGTH,
                             dummy, nil))
        then begin
           pOut := PSENDCMDOUTPARAMS(buffer + sizeof (SRB_IO_CONTROL)); //!TOCHECK
           pId := PIDSECTOR(@pOut^.bBuffer[0]);
           if (pId^.sModelNumber[0] <> Chr(0) ) then begin
              pIdSectorPtr := PWord(pId);
              for I := 0 to 256-1 do
                 DiskData[I] := PDataArray(pIdSectorPtr)[I];
              PrintIdeInfo (DiskData);
              FInfoAvailable := True;
           end;

     end;
     CloseHandle(FFileHandle);
  end;
end;

procedure THDScsiInfo.SetDriveNumber(const Value: Byte);
begin
  FDriveNumber := Value;
  ReadInfo;
end;

end.

Sample usage: 样品用法:

procedure ReadIdeDriveAsScsiDriveInNT;
var
  DriveNumber: Byte;
  HDDInfo: THDScsiInfo;
begin
  HDDInfo := THDScsiInfo.Create();
  try
    for DriveNumber := 0 to MAX_IDE_DRIVES - 1 do
    try
      HDDInfo.DriveNumber := DriveNumber;
      if HDDInfo.IsInfoAvailable then begin
        Writeln('Available Drive: ', HDDInfo.DriveNumber);
        Writeln('ControllerType: ', HDDInfo.ControllerType);
        Writeln('DriveMS: ', HDDInfo.DriveMS);
        Writeln('DriveModelNumber: ', HDDInfo.DriveModelNumber);
        Writeln('ControllerBufferSizeOnDrive: ', HDDInfo.ControllerBufferSizeOnDrive);
        Writeln('DriveType: ', HDDInfo.DriveType);
        Writeln('DriveSizeBytes: ', HDDInfo.DriveSizeBytes);
        Writeln('ProductRevision: ', HDDInfo.ProductRevision);
        Writeln('SerialNumber: ', HDDInfo.SerialNumber);
      end;
    except
      on E: Exception do
        Writeln(Format('DriveNumber %d, %s: %s', [DriveNumber, E.ClassName, E.Message]));
    end;
  finally
    HDDInfo.Free;
  end;
end;

begin
  ReadIdeDriveAsScsiDriveInNT;
  Write('Press <Enter>');
end.

This works great with my WD disk. 这适用于我的WD磁盘。

Posting this purely for completeness sake, and to possibly satisfy those interested or die hard hardware fanatics. 纯粹为了完整性而发布这个,并且可能满足那些感兴趣或死硬的硬件狂热者。

I do not have a Pascal compiler at my disposal to test these routines on current Windows systems, but I do know this code worked back in the DOS era. 我没有使用Pascal编译器来测试当前Windows系统上的这些例程,但我知道这段代码在DOS时代可以运行。 Maybe it still works from a command prompt window. 也许它仍然可以在命令提示符窗口中运行。

Pascal code: Pascal代码:

uses
  Dos, Crt;

type
  SerNoType = record
    case Integer of
      0: (SerNo1, SerNo2: Word);
      1: (SerNo: Longint);
  end;

  DiskSerNoInfoType = record
    Infolevel: Word;
    VolSerNo: SerNoType;
    VolLabel: array[1..11] of Char;
    FileSys: array[1..8] of Char;
  end;

function HexDigit(N: Byte): Char;
begin
  if N < 10 then
    HexDigit := Chr(Ord('0') + N)
  else
    HexDigit := Chr(Ord('A') + (N - 10));
end;

function GetVolSerialNo(DriveNo: Byte): String;
var
  ReturnArray: DiskSerNoInfoType;
  Regs: Registers;
begin
  with Regs do
  begin
    AX := $440d;
    BL := DriveNo;
    CH := $08;
    CL := $66;
    DS := Seg(ReturnArray);
    DX := Ofs(ReturnArray);
    Intr($21, Regs);
    if (Flags and FCarry) <> 0 then
      GetVolSerialNo := ''
    else
      with ReturnArray.VolSerNo do
        GetVolSerialNo :=
          HexDigit(Hi(SerNo2) div 16) + HexDigit(Hi(SerNo2) mod 16) +
          HexDigit(Lo(SerNo2) div 16) + HexDigit(Lo(SerNo2) mod 16) +
          HexDigit(Hi(SerNo1) div 16) + HexDigit(Hi(SerNo1) mod 16) +
          HexDigit(Lo(SerNo1) div 16) + HexDigit(Lo(SerNo1) mod 16);
  end;
end;

procedure PutVolSerialNo(DriveNo: Byte; SerialNo: Longint);
var
  ReturnArray: DiskSerNoInfoType;
  Regs: Registers;
begin
  with Regs do
  begin
    AX := $440d;
    BL := DriveNo;
    CH := $08;
    CL := $66;
    DS := Seg(ReturnArray);
    DX := Ofs(ReturnArray);
    Intr($21, Regs);
    if (Flags and FCarry) = 0 then
    begin
      ReturnArray.VolSerNo.SerNo := SerialNo;
      AH := $69;
      BL := DriveNo;
      AL := $01;
      DS := Seg(ReturnArray);
      DX := Ofs(ReturnArray);
      Intr($21, Regs);
    end;
  end;
end;

Please feel free to update this answer in order to get it working (if possible at all) in Delphi. 请随时更新此答案,以便在Delphi中使其正常工作(如果可能的话)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM