[BlueLeaf1336]> PROGRAM>

IDEのHDDシリアル番号取得?

historyTOP

2005/12/17:作成

overviewTOP

まず最初に。このページの内容に独自性はありません。完全無欠にパクリです。

USERS GROUP : Delphi メーリングリスト で [Delphi:87049] ハードディスクシリアルNo という投稿があって、「GetIdeDiskSerialNumber」という関数があるらしいのですが...という質問がされていました。

Googleで検索してみると、海外の特に英語でない国のサイトばかりがヒットします。でもそこは人口言語Delphiですので、自然言語箇所は全く不明でも問題ありません。そのまま実行してみるとなにやら文字列が取得できました。

どうやったらコレがシリアル番号を意味しているかを判定する方法はわからないし、何に使うのかももう一つわからないし、Googleの「GetIdeDiskSerialNumber の検索結果 約 551 件」のおそらく全てで、同じ構造同じ変数名のサンプルコードが使いまわされていると思われ、今さら何を勝手に複製するのか微妙なところですが、いつかのために、他の全てのサイトが参照できなくなった時のために、ここにも書いておくことにします。

ただ、わかる範囲でコメントをつけることにしました。焼け石に水でしたが。構造体の定義を何処から持ってきたのかとか...。

根本的に気になる箇所が一点。

hDevice := CreateFile('\\.\Scsi0:', ...
とあって、職人フォーラム::記事閲覧(一覧表示)- HDD ATA 接続デバイスから SMART の取得をする方法 にあるように、それって「SCSIデバイスに、SCSIコマンドを発行」してるんじゃないのか、と。でも今は、SCSIじゃなくて、IDEのことを話してるんじゃないのかと。

わかりません。

コードTOP

//  [Delphi:87049] ハードディスクシリアルNo
//  [Delphi:87050] Re: ハードディスクシリアルNo
//  [Delphi:87052] Re: ハードディスクシリアルNo

//  GetIdeDiskSerialNumber
//  http://www.cdown.net/Info/32413.html

//  Active Delphi
//  http://www.activedelphi.com.br/print.php?sid=214

//  DELPHI FAQ
//  http://delphi.cartall.com.pl/Pytania/pyt41.htm

//  GetIdeDiskSerialNumber でGoogle検索するとかなりの数のサイトが
//  ヒットするけど、どのサイトも一言一句違わず(っぽい)同じ構成。
//  最初にかきあげた誰かのコードが広まってるような感じ。


//  関係していそうなURL

//  Storage Devices: Windows Driver Kit 
//  I/O Requests for Mass Storage Drivers

//  http://msdn.microsoft.com/library/default.asp?
//      url=/library/en-us/Storage_r/hh/Storage_r/k307_4cd1d823-5899-43f9-8076-3ebd8dfe97ba.xml.asp


unit HddSerial;

interface

//  (名前からして)IDEのHDDのシリアル#を取得する関数
function GetIdeDiskSerialNumber(): String;

implementation

uses
    Windows, SysUtils;


type
    //-------------------------------------------------------------------------
    //  Storage Devices: Windows DDK
    //  SRB_IO_CONTROL

    //  http://msdn.microsoft.com/library/default.asp?
    //  url=/library/en-us/storage/hh/storage/structs-scsibus_d7cd0432-d4be-4609-a3f9-91ef842caf7e.xml.asp

    //  This structure is used by applications to send requests directly to an
    //  application-dedicated HBA. Note that such an application also must set
    //  up requests to program its dedicated HBA.

    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;

    //-------------------------------------------------------------------------
    //  Storage Devices: Windows Driver Kit
    //  IDEREGS 

    //  http://msdn.microsoft.com/library/default.asp?
    //      url=/library/en-us/Storage_r/hh/Storage_r/structs-IDE_aeab294c-9363-4207-bbcb-d9d442ab5c92.xml.asp

    //  The IDEREGS structure is used to report the contents of the IDE controller
    //  registers

    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;

    //-------------------------------------------------------------------------
    //  Storage Devices: Windows DDK
    //  SENDCMDINPARAMS

    //  http://msdn.microsoft.com/library/default.asp?
    //      url=/library/en-us/storage/hh/storage/structs-IDE_b80faf9d-dfcf-4eac-b0be-fb18964c4c2b.xml.asp

    //  The SENDCMDINPARAMS structure contains the input parameters for the
    //  SMART_SEND_DRIVE_COMMAND request.
    //  The SMART_SEND_DRIVE_COMMAND is used to send a Self-Monitoring Analysis
    //  and Reporting Technology (SMART) commands to a device. 

    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;

//-----------------------------------------------------------------------------
//  バイトオーダー変更
procedure ChangeByteOrder(var Data; const 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;

//-----------------------------------------------------------------------------
//  IDEのHDDのシリアル#を取得する
function GetIdeDiskSerialNumber(): String;
var
    hDevice: THandle;
    cbBytesReturned: DWORD;
    pInData: PSendCmdInParams;
    pOutData: Pointer; // PSendCmdOutParams
    Buffer: array[0..BufferSize-1] of Byte;
    srbControl: TSrbIoControl absolute Buffer;
    //  absolute で他の変数(この場合Buffer)と同じアドレスを持つ変数を宣言できる
begin
    //  空文字で初期化
    Result := '';

    //  バッファを初期化
    FillChar(Buffer, BufferSize, #0);

    //---------------------------------------------------------------
    //  Windows NT, Windows 2000 の場合
    //---------------------------------------------------------------

    if (Win32Platform = VER_PLATFORM_WIN32_NT) then
    begin
        //  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;

            //  デバイスドライバへ制御コードを直接送信
            //  http://www.microsoft.com/japan/msdn/library/default.asp?
            //      url=/japan/msdn/library/ja/jphard/html/_win32_DeviceIoControl.asp
            if not DeviceIoControl(
                hDevice,                //  デバイスハンドル
                IOCTL_SCSI_MINIPORT,    //  実行する動作の制御コード
                @Buffer,                //  入力バッファのポインタ
                BufferSize,             //  入力バッファのバイトサイズ
                @Buffer,                //  出力バッファのポインタ
                BufferSize,             //  出力バッファのバイトサイズ
                cbBytesReturned,        //  バイト数を受け取る変数へのポインタ
                nil)                    //  非同期動作を表す構造体へのポインタ
            then Exit;

        finally
            CloseHandle(hDevice);
        end;
    end

    //---------------------------------------------------------------
    //  Windows 95 OSR2, Windows 98 の場合
    //---------------------------------------------------------------

    else
    begin
        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;

    //  末尾のNULLをはつる
    Result := Trim(Result);
end;

end.

使い方TOP

こんな感じで。

uses
    HDDSerial;

procedure TForm1.FormCreate(Sender: TObject);
begin
    ShowMessage(GetIdeDiskSerialNumber());
end;

実行結果TOP

上の使い方をそのまま実行したら、シリアル番号らしき文字列を表示します(下のは、ダイアログを CTRL+C でコピったものです。念のため。別にみられてもよさそうなので、コピってそのまま貼り付けてます)。

---------------------------
Project1
---------------------------
0191J1FN200091
---------------------------
OK
---------------------------

EOFTOP