Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This code makes three attempts at obtaining the serial number:</p> <ol> <li>Using <code>IOCTL_STORAGE_QUERY_PROPERTY</code>.</li> <li>Using <code>SMART_RCV_DRIVE_DATA</code>.</li> <li>Using <code>IOCTL_SCSI_PASS_THROUGH</code>.</li> </ol> <p>This code works for me on 64-bit:</p> <pre><code>' PhysicalDrive.vb Option Strict On Option Explicit On Imports System.Runtime.InteropServices Imports System.Text Imports System.ComponentModel Imports Microsoft.Win32.SafeHandles Public Class PhysicalDrive #Region "Win32 Definitions" &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure IDEREGS Public bFeaturesReg As Byte Public bSectorCountReg As Byte Public bSectorNumberReg As Byte Public bCylLowReg As Byte Public bCylHighReg As Byte Public bDriveHeadReg As Byte Public bCommandReg As Byte Public bReserved As Byte End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure SENDCMDINPARAMS Public cBufferSize As Int32 Public irDriveRegs As IDEREGS Public bDriveNumber As Byte &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)&gt; _ Public bReserved As Byte() &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)&gt; _ Public dwReserved As Int32() &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)&gt; _ Public bBuffer As Byte() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure DRIVERSTATUS Public bDriverError As Byte Public bIDEError As Byte &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)&gt; _ Public bReserved As Byte() &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)&gt; _ Public dwReserved As Int32() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure SENDCMDOUTPARAMS Public cBufferSize As Int32 Public DriverStatus As DRIVERSTATUS &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=IDENTIFY_BUFFER_SIZE)&gt; _ Public bBuffer As Byte() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure GETVERSIONINPARAMS Public bVersion As Byte Public bRevision As Byte Public bReserved As Byte Public bIDEDeviceMap As Byte Public fCapabilities As Int32 &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)&gt; _ Public dwReserved As Int32() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure STORAGE_PROPERTY_QUERY Public PropertyId As Int32 Public QueryType As Int32 &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)&gt; _ Public AdditionalParameters As Byte() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure STORAGE_DEVICE_DESCRIPTOR Public Version As Int32 Public Size As Int32 Public DeviceType As Byte Public DeviceTypeModifier As Byte Public RemovableMedia As Byte Public CommandQueueing As Byte Public VendorIdOffset As Int32 Public ProductIdOffset As Int32 Public ProductRevisionOffset As Int32 Public SerialNumberOffset As Int32 Public BusType As Byte Public RawPropertiesLength As Int32 &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=10240)&gt; _ Public RawDeviceProperties As Byte() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure SCSI_PASS_THROUGH Public Length As Int16 Public ScsiStatus As Byte Public PathId As Byte Public TargetId As Byte Public Lun As Byte Public CdbLength As Byte Public SenseInfoLength As Byte Public DataIn As Byte Public DataTransferLength As Int32 Public TimeOutValue As Int32 Public DataBufferOffset As IntPtr Public SenseInfoOffset As Int32 &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)&gt; _ Public Cdb As Byte() End Structure &lt;StructLayout(LayoutKind.Sequential)&gt; _ Private Structure SCSI_PASS_THROUGH_WITH_BUFFER Public Spt As SCSI_PASS_THROUGH Public Filler As Int32 &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=64)&gt; _ Public Buffer As Byte() End Structure &lt;DllImport("kernel32.dll", SetLastError:=True)&gt; _ Private Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As Int32, ByVal dwShareMode As Int32, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As Int32, ByVal dwFlagsAndAttributes As Int32, ByVal hTemplateFile As IntPtr) As SafeFileHandle End Function &lt;DllImport("kernel32.dll", SetLastError:=True)&gt; _ Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, &lt;[In]()&gt; ByRef lpInBuffer As SENDCMDINPARAMS, ByVal nInBufferSize As Int32, &lt;[In](), Out()&gt; ByRef lpOutBuffer As SENDCMDOUTPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 End Function &lt;DllImport("kernel32.dll", SetLastError:=True)&gt; _ Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, ByVal lpInBuffer As IntPtr, ByVal nInBufferSize As Int32, &lt;[In](), Out()&gt; ByRef lpOutBuffer As GETVERSIONINPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 End Function &lt;DllImport("kernel32.dll", SetLastError:=True)&gt; _ Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, &lt;[In]()&gt; ByRef lpInBuffer As STORAGE_PROPERTY_QUERY, ByVal nInBufferSize As Int32, &lt;[In](), Out()&gt; ByRef lpOutBuffer As STORAGE_DEVICE_DESCRIPTOR, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 End Function &lt;DllImport("kernel32.dll", SetLastError:=True)&gt; _ Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, &lt;[In]()&gt; ByRef lpInBuffer As SCSI_PASS_THROUGH_WITH_BUFFER, ByVal nInBufferSize As Int32, &lt;[In](), Out()&gt; ByRef lpOutBuffer As SCSI_PASS_THROUGH_WITH_BUFFER, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32 End Function Private Const OPEN_EXISTING As Int32 = 3 Private Const GENERIC_READ As Int32 = &amp;H80000000 Private Const GENERIC_WRITE As Int32 = &amp;H40000000 Private Const FILE_SHARE_READ As Int32 = &amp;H1 Private Const FILE_SHARE_WRITE As Int32 = &amp;H2 Private Const FILE_SHARE_DELETE As Int32 = &amp;H4 Private Const SMART_GET_VERSION As Int32 = &amp;H74080 Private Const SMART_RCV_DRIVE_DATA As Int32 = &amp;H7C088 Private Const ID_CMD As Int32 = &amp;HEC Private Const IDENTIFY_BUFFER_SIZE As Int32 = 512 Private Const CAP_SMART_CMD As Int32 = &amp;H4 Private Const IOCTL_STORAGE_QUERY_PROPERTY As Int32 = &amp;H2D1400 Private Const IOCTL_SCSI_PASS_THROUGH As Int32 = &amp;H4D004 Private Const SCSI_IOCTL_DATA_IN As Int32 = &amp;H1 Private Const PropertyStandardQuery As Int32 = 0 Private Const StorageDeviceProperty As Int32 = 0 Private Const ERROR_INVALID_FUNCTION As Int32 = &amp;H1 #End Region Public Shared Function GetSerialNumberUsingStorageQuery(ByVal diskNumber As Integer) As String Using hDisk As SafeFileHandle = OpenDisk(diskNumber) Dim iBytesReturned As Int32 Dim spq As New STORAGE_PROPERTY_QUERY() Dim sdd As New STORAGE_DEVICE_DESCRIPTOR() spq.PropertyId = StorageDeviceProperty spq.QueryType = PropertyStandardQuery If DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, spq, Marshal.SizeOf(spq), sdd, Marshal.SizeOf(sdd), iBytesReturned, 0) = 0 Then Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)") End If Dim result As New StringBuilder() If sdd.SerialNumberOffset &gt; 0 Then Dim rawDevicePropertiesOffset As Integer = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length Dim pos As Integer = sdd.SerialNumberOffset - rawDevicePropertiesOffset While pos &lt; iBytesReturned And sdd.RawDeviceProperties(pos) &lt;&gt; 0 result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1)) pos += 1 End While End If Return result.ToString().Trim() End Using End Function Public Shared Function GetSerialNumberUsingSmart(ByVal diskNumber As Integer) As String Using hDisk As SafeFileHandle = OpenDisk(diskNumber) If IsSmartSupported(hDisk) Then Dim iBytesReturned As Int32 Dim sci As New SENDCMDINPARAMS Dim sco As New SENDCMDOUTPARAMS sci.irDriveRegs.bCommandReg = ID_CMD sci.bDriveNumber = CByte(diskNumber) sci.cBufferSize = IDENTIFY_BUFFER_SIZE If DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, sci, Marshal.SizeOf(sci), sco, Marshal.SizeOf(sco), iBytesReturned, 0) = 0 Then Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)") End If Dim result As New StringBuilder() For index As Integer = 20 To 39 Step 2 result.Append(Encoding.ASCII.GetString(sco.bBuffer, index + 1, 1)) result.Append(Encoding.ASCII.GetString(sco.bBuffer, index, 1)) Next Return result.ToString().Trim() Else Return String.Empty End If End Using End Function Public Shared Function GetSerialNumberUsingScsiPassThrough(ByVal diskNumber As Integer) As String Using hDisk As SafeFileHandle = OpenDisk(diskNumber) Dim iBytesReturned As Int32 Dim spt As New SCSI_PASS_THROUGH_WITH_BUFFER spt.Spt.Length = CShort(Marshal.SizeOf(spt.Spt)) spt.Spt.CdbLength = 16 spt.Spt.DataIn = SCSI_IOCTL_DATA_IN spt.Spt.DataTransferLength = 64 spt.Spt.DataBufferOffset = New IntPtr(Marshal.SizeOf(spt) - 64) spt.Spt.TimeOutValue = 60 Dim cdb(15) As Byte cdb(0) = &amp;H12 ' INQUIRY cdb(1) = &amp;H1 ' EVPD bit cdb(2) = &amp;H80 ' Page code (indicates Serial Number) cdb(4) = 64 ' Allocation length spt.Spt.Cdb = cdb If DeviceIoControl(hDisk, IOCTL_SCSI_PASS_THROUGH, spt, Marshal.SizeOf(spt), spt, Marshal.SizeOf(spt), iBytesReturned, 0) = 0 Then Dim iErrorCode As Int32 = Marshal.GetLastWin32Error() If iErrorCode &lt;&gt; ERROR_INVALID_FUNCTION Then Throw CreateWin32Exception(iErrorCode, "DeviceIoControl(IOCTL_SCSI_PASS_THROUGH)") End If End If Dim result As New StringBuilder() Dim pos As Integer = IntPtr.Size While pos &lt; spt.Spt.DataTransferLength And spt.Buffer(pos) &lt;&gt; 0 result.Append(Encoding.ASCII.GetString(spt.Buffer, pos, 1)) pos += 1 End While Return result.ToString().Trim() End Using End Function Private Shared Function CreateWin32Exception(ByVal errorCode As Int32, ByVal context As String) As Win32Exception Dim win32Exception As New Win32Exception(errorCode) win32Exception.Data("Context") = context Return win32Exception End Function Private Shared Function OpenDisk(ByVal diskNumber As Integer) As SafeFileHandle Dim hDevice As SafeFileHandle = CreateFile(String.Format("\\.\PhysicalDrive{0}", diskNumber), GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero) If (Not hDevice.IsInvalid) Then Return hDevice Else Throw CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile") End If End Function Private Shared Function IsSmartSupported(ByVal hDisk As SafeFileHandle) As Boolean Dim iBytesReturned As Int32 Dim gvi As New GETVERSIONINPARAMS If DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, gvi, Marshal.SizeOf(gvi), iBytesReturned, 0) = 0 Then Return False End If Return (gvi.fCapabilities And CAP_SMART_CMD) &gt; 0 End Function End Class </code></pre> <p>And here's the code to call it:</p> <pre><code>' MainModule.vb Module MainModule Sub Main() Console.WriteLine("{0}-bit runtime.", IntPtr.Size * 8) For drive As Integer = 0 To 4 Try Console.WriteLine("Drive {0}, SMART: [{1}]", drive, PhysicalDrive.GetSerialNumberUsingSmart(drive)) Console.WriteLine("Drive {0}, Storage Query: [{1}]", drive, PhysicalDrive.GetSerialNumberUsingStorageQuery(drive)) Console.WriteLine("Drive {0}, SCSI Pass Through: [{1}]", drive, PhysicalDrive.GetSerialNumberUsingScsiPassThrough(drive)) Catch ex As Exception If ex.Data("Context") IsNot Nothing Then Console.Error.Write("{0} failed: ", ex.Data("Context")) Console.Error.WriteLine(ex.Message) End Try Next End Sub End Module </code></pre> <p><strong>EDIT</strong> - I've changed the main method to display the results of each attempt for comparison. This will hopefully illustrate how hit and miss these techniques can be.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload