C # / Native: чтение последовательного порта жесткого диска с помощью сквозного подключения SCSI

Я написал три разных метода, которые используют собственные вызовы CreateFile и DeviceIoControl для получения серийного номера жесткого диска (а не номера модели). Первый использует S.M.A.R.T., второй использует Storage Query, а третий использует SCSI PassThrough. Большая часть моего кода основана на содержимом эта ветка (с некоторыми исправлениями и улучшениями).

Вот результаты, которые я получаю с помощью утилиты diskid32:

Trying to read the drive IDs using physical access with admin rights

Drive Model Number________________: [ST975XXXXX]
Drive Serial Number_______________: [            6WS2XXXX]

Trying to read the drive IDs using physical access with zero rights

Product Id = [ST975XXXXX]
Serial Number = [6WS2XXXX]

Trying to read the drive IDs using Smart

Drive Model Number________________: [ST975XXXXX]
Drive Serial Number_______________: [            6WS2XXXX]

Вот результаты, полученные с использованием моих методов:

S.M.A.R.T. = 6WS2XXXX
Storage Query = 6WS2XXXX
SCSI PassThrough = ST975XXXXX

Что ж ... Хьюстон, у нас тут проблема. С помощью первых двух методов я получаю правильный серийный номер. С последним я получаю номер модели, что очень плохо. Вот мой код:

--- METHOD ---

internal static String GetHardDiskSerialSCSIPassthrough(SafeFileHandle deviceHandle)
{
    IntPtr bufferPointer = IntPtr.Zero;
    String serial = String.Empty;
    UInt32 bytesReturned;

    SCSIPassthroughBuffered bspt = new SCSIPassthroughBuffered();
    bspt.SPT.Length = (UInt16)Marshal.SizeOf(bspt.SPT);
    bspt.SPT.CommandDescriptorBlockLength = 16;
    bspt.SPT.DataIn = 0x1;
    bspt.SPT.DataTransferLength = 64;
    bspt.SPT.DataBufferOffset = new IntPtr(Marshal.SizeOf(bspt) - 64);
    bspt.SPT.TimeOutValue = 60;
    bspt.SPT.CommandDescriptorBlock = new Byte[] { 0x12, 0x1, 0x80, 0x0, 64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };

    Int32 bufferSize = Marshal.SizeOf(bspt);

    try
    {
        bufferPointer = Marshal.AllocHGlobal(bufferSize);

        Marshal.StructureToPtr(bspt, bufferPointer, true);

        if (DeviceIoControl(deviceHandle, 0x4D004, bufferPointer, (UInt32)bufferSize, bufferPointer, (UInt32)bufferSize, out bytesReturned, IntPtr.Zero) && (bytesReturned > 0))
        {
            SCSIPassthroughBuffered result = (SCSIPassthroughBuffered)Marshal.PtrToStructure(bufferPointer, typeof(SCSIPassthroughBuffered));
            serial = Encoding.ASCII.GetString(result.Buffer, 0, result.Buffer.Length).Replace("\0", String.Empty).Trim();
        }
    }
    finally
    {
        Marshal.FreeHGlobal(bufferPointer);
    }

    return serial;
}

--- STRUCTURES ---

[StructLayout(LayoutKind.Sequential)]
private struct SCSIPassthrough
{
    public UInt16 Length;
    public Byte SCSIStatus;
    public Byte PathID;
    public Byte TargetID;
    public Byte LogicalUnitNumber;
    public Byte CommandDescriptorBlockLength;
    public Byte SenseInfoLength;
    public Byte DataIn;
    public UInt32 DataTransferLength;
    public UInt32 TimeOutValue;
    public IntPtr DataBufferOffset;
    public UInt32 SenseInfoOffset;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public Byte[] CommandDescriptorBlock;
}

[StructLayout(LayoutKind.Sequential)]
private struct SCSIPassthroughBuffered
{
    public SCSIPassthrough SPT;
    public UInt32 Filler;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    public Byte[] Buffer;
}

Что я делаю неправильно? Может быть, используете неправильную CDB?


person Tommaso Belluzzo    schedule 08.05.2013    source источник


Ответы (1)


Ваш код действительно работает для меня нормально, т.е. он возвращает те же данные, что и другие методы, описанные в указанном потоке. Единственное изменение, которое мне пришлось внести, было от:

serial = Encoding.ASCII.GetString(result.Buffer, 0, result.Buffer.Length)
                       .Replace("\0", String.Empty)
                       .Trim();

To:

serial = Encoding.ASCII.GetString(result.Buffer, 0, result.Buffer.Length)
                       .Substring(IntPtr.Size)
                       .Replace("\0", String.Empty)
                       .Trim();
person Dave Cluderay    schedule 09.05.2013
comment
Какое у вас устройство? - person Tommaso Belluzzo; 10.05.2013
comment
64-битная Windows Server 2008 R2 на HP ML115, диск - GB0160CAABV. Даже diskid32 не дает единообразных результатов для всех дисков, использующих эти методы - это кажется очень удачным из прошлого опыта. - person Dave Cluderay; 10.05.2013