DELPHI:HardDisk不等制造序列号源代码修改

时间:2015-11-25 23:54:01

标签: delphi unicode ansi hard-drive

我有一个代码来获取硬盘制造序列号,它总是适合我... 但是在从Delphi 7到Delphi 2010的版本之后,这段代码不再起作用了。 当然,我认为这个原因与DELICI版本支持的UNICODE和ANSI有关。 但现在,转换为DELPHI 2010对我来说很难...... 如果您有一个适用于DELPHI unicode支持版本的代码示例, 或者可以修改我的代码以便轻松地用于DELPHI unicode版本,请给我帮助.. 感谢阅读我的帖子...... 以下是我在DELPHI 7上运行良好的原始代码。

unit HDD_Serial;

interface

uses Windows, SysUtils;

function GetHddSerial: String;

implementation

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;

function GetDeviceHandle(sDeviceName: String): THandle;
begin
  Result := CreateFile(PChar('\\.\' + sDeviceName), GENERIC_READ or
    GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
    OPEN_EXISTING, 0, 0);
end;

function ScsiHddSerialNumber(DeviceHandle: THandle): String;
{$ALIGN ON}
type
  TScsiPassThrough = record
    Length: Word;
    ScsiStatus: Byte;
    PathId: Byte;
    TargetId: Byte;
    Lun: Byte;
    CdbLength: Byte;
    SenseInfoLength: Byte;
    DataIn: Byte;
    DataTransferLength: ULONG;
    TimeOutValue: ULONG;
    DataBufferOffset: DWORD;
    SenseInfoOffset: ULONG;
    Cdb: Array [0 .. 15] of Byte;
  end;

  TScsiPassThroughWithBuffers = record
    spt: TScsiPassThrough;
    bSenseBuf: Array [0 .. 31] of Byte;
    bDataBuf: Array [0 .. 191] of Byte;
  end;
  { ALIGN OFF }
var
  dwReturned: DWORD;
  len: DWORD;
  Buffer: Array [0 .. sizeof(TScsiPassThroughWithBuffers) +
    sizeof(TScsiPassThrough) - 1] of Byte;
  sptwb: TScsiPassThroughWithBuffers absolute Buffer;
begin
  Result := '';
  FillChar(Buffer, sizeof(Buffer), #0);
  with sptwb.spt do
  begin
    Length := sizeof(TScsiPassThrough);
    CdbLength := 6; // CDB6GENERIC_LENGTH
    SenseInfoLength := 24;
    DataIn := 1; // SCSI_IOCTL_DATA_IN
    DataTransferLength := 192;
    TimeOutValue := 2;
    DataBufferOffset := PChar(@sptwb.bDataBuf) - PChar(@sptwb);
    SenseInfoOffset := PChar(@sptwb.bSenseBuf) - PChar(@sptwb);
    Cdb[0] := $12; // OperationCode := SCSIOP_INQUIRY;
    Cdb[1] := $01; // Flags := CDB_INQUIRY_EVPD; Vital product data
    Cdb[2] := $80; // PageCode Unit serial number
    Cdb[4] := 192; // AllocationLength
  end;
  len := sptwb.spt.DataBufferOffset + sptwb.spt.DataTransferLength;
  if DeviceIoControl(DeviceHandle, $0004D004, @sptwb, sizeof(TScsiPassThrough),
    @sptwb, len, dwReturned, nil) and ((PChar(@sptwb.bDataBuf) + 1)^ = #$80)
  then
    SetString(Result, PChar(@sptwb.bDataBuf) + 4,
      Ord((PChar(@sptwb.bDataBuf) + 3)^));
end;

function GetHddSerial: String;
var
  NumTry: Byte;

  FinalStr: String;
  hDevice: THandle;
  sDeviceName: String;
begin
  NumTry := 1;
  Repeat
    Case NumTry Of
      1:
        Begin
          FinalStr := Trim(GetIdeDiskSerialNumber);
        End;
      2:
        Begin
          sDeviceName := 'C:';
          hDevice := GetDeviceHandle(sDeviceName);
          If hDevice <> INVALID_HANDLE_VALUE Then
          Begin
            Try
              FinalStr := Trim(ScsiHddSerialNumber(hDevice));
            Finally
              CloseHandle(hDevice);
            End;
          End;
        End;
      3:
        Begin
          FinalStr := 'Error!';
        End;
    End;

    Inc(NumTry);
  Until (FinalStr <> '') Or (NumTry > 3);

  Result := FinalStr;
end;

end.

1 个答案:

答案 0 :(得分:2)

除了所需的Pchar - &gt; PAnsiChar修复,一个容易错过的问题是

  System.Move('SCSIDISK', srbControl.Signature, 8);

该字符串作为PChar传递给move函数。为了解决这个问题,我把它改成了一个类型化的const

const
  kScsiDisk : AnsiString = 'SCSIDISK';
[...]
  System.Move(kScsiDisk , srbControl.Signature, 8);

修好后,转换PChar - &gt; PAnsiChar和Char - &gt; AnsiChar,代码开始在我的系统上返回一个有效值。

GetDeviceHandle似乎也存在问题。您正在使用'\。\ C:'打开设备,但据我所知,您应该使用'\。\ PhysicalDriveX'(其中X是驱动器的序列)。再说一次,我不太熟悉以这种方式查询设备,所以我可能会遗漏一些东西。