2015-11-25 33 views
-4

我有一個代碼來獲取硬盤製造序列號,它總是適合我... 但是從Delphi 7到Delphi 2010的版本,此代碼doesn'工作了。 當然,我認爲由DELPHI版本支持的UNICODE和ANSI之間的區別原因。 但現在,轉換爲德爾福2010年是很難我... 如果你有這樣的代碼示例,在DELPHI的Unicode支持版本的作品, 或者可以修改我的代碼可以輕鬆地使用DELPHI Unicode版本,請給我幫忙。 。 感謝閱讀我的文章... 下面是我的原代碼,在DELPHI 7DELPHI:HardDisk unequal製造序列號源代碼修改

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. 
+0

問題是什麼?將所有'Char'類型更改爲'AnsiChar',將所有'String'類型更改爲'AnsiString','PChar'更改爲'PAnsiChar',並且Unicode不是問題。 –

+0

你說過嗎?我認爲你很容易說話。我已經像你說過的那樣嘗試過了,但仍然存在問題......理解? – fangrenxing

+1

你說沒有提到嘗試任何東西。如果您已經嘗試過,但它不起作用,您需要準確告訴我們您嘗試的是什麼,以及哪些結果不起作用。理解?堆棧溢出不是代碼寫入或轉換服務。 @KenWhite只是基於我們可以看到的解決方案來回答你的問題。你已經知道這是一個unicode問題,並且有很多文檔解釋如何去做這個轉變。 –

回答

2

除了必要的PCHAR效果很好 - > PAnsiChar修復,一個問題是很容易錯過的

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

該字符串被傳入一個PChar到move函數。爲了解決這個問題,我把它改成一個類型常量

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

固定的是,轉換後PChar類型 - > PAnsiChar和字符 - > ANSIChar類型,代碼開始返回我的系統上的有效價值。

還有似乎與GetDeviceHandle的問題。您使用'\。\ C:'來打開設備,但根據我所知道的,您應該使用'\。\ PhysicalDriveX'(其中X是驅動器的序列)。然後再次,我不熟悉以這種方式查詢設備,所以我可能會錯過一些東西。

+0

我認爲他還可以使用TEncoding類功能:-) 雖好老校友移動是如此隸:-D –

+0

我個人將避免串。它只是一個8字節的數組。將它聲明爲一個const並賦值。對我來說,這比清理文本編碼要乾淨得多。 –