Option Strict On Option Explicit On Imports System.Runtime.InteropServices Imports Microsoft.Win32.SafeHandles Imports System.Security Imports System.ComponentModel Imports System.Text Module Module1 <SuppressUnmanagedCodeSecurity()> _ Private Class NativeMethods <DllImport("kernel32", SetLastError:=True)> _ Public Shared Function CreateFile( _ ByVal FileName As String, _ ByVal DesiredAccess As Integer, _ ByVal ShareMode As Integer, _ ByVal SecurityAttributes As IntPtr, _ ByVal CreationDisposition As Integer, _ ByVal FlagsAndAttributes As Integer, _ ByVal hTemplateFile As IntPtr) As SafeFileHandle End Function <DllImport("kernel32.dll", SetLastError:=True)> _ Friend Shared Function DeviceIoControl( _ ByVal deviceHandle As SafeFileHandle, _ ByVal controlCode As Integer, _ ByRef inBuffer As ScsiPassThroughWithBuffers, _ ByVal inBufferSize As Integer, _ ByRef outBuffer As ScsiPassThroughWithBuffers, _ ByVal outBufferSize As Integer, _ ByRef bytesReturned As Integer, _ ByVal overlapped1 As IntPtr) As Boolean End Function End Class <StructLayout(LayoutKind.Sequential, pack:=8)> _ Private Structure ScsiPassThrough Public Length As Short Public ScsiStatus As Byte Public PathId As Byte ' port / bus Public TargetId As Byte ' controller Public Lun As Byte ' Lun Public CdbLength As Byte Public SenseInfoLength As Byte Public DataIn As Byte Public DataTransferLength As Integer Public TimeOutValue As Integer Public DataBufferOffset As IntPtr Public SenseInfoOffset As Integer <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _ Public Cdb() As Byte Public Sub Init() Me.Length = CShort(Marshal.SizeOf(GetType(ScsiPassThrough))) CdbLength = 6 ' Size of our command structure SenseInfoLength = 32 ' Size of our buffer that may be filled with error/status info DataIn = 1 ' SCSI_IOCTL_DATA_IN DataTransferLength = 128 ' Size of our buffer that gets filled with informaion TimeOutValue = 10 ' TargetId, PathId and Lun are filled on return. Don't mean anything on the way out. DataBufferOffset = Marshal.OffsetOf(GetType(ScsiPassThroughWithBuffers), "Data") SenseInfoOffset = Marshal.OffsetOf(GetType(ScsiPassThroughWithBuffers), "Sense").ToInt32 Cdb = New Byte(15) {} Cdb(0) = &H12 Cdb(4) = 128 End Sub End Structure <StructLayout(LayoutKind.Sequential, pack:=8)> _ Private Structure ScsiPassThroughWithBuffers Public Spt As ScsiPassThrough <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> _ Public Sense() As Byte <MarshalAs(UnmanagedType.ByValArray, SizeConst:=128)> _ Public Data() As Byte Public Sub Init() Sense = New Byte(31) {} Data = New Byte(127) {} Spt = New ScsiPassThrough Spt.Init() End Sub End Structure Private Enum PeripheralDeviceType Sbc2 = 0 ' direct access block device (disk) Ssc2 = 1 ' sequential access block device (tape) Ssc = 2 ' Printer Spc2 = 3 ' Processor Sbc = 4 ' Write once Mmc = 5 ' CD/DVD etc SbcOptical = 7 ' Optical memory device Smc2 = 8 ' Medium changer scc2 = &HC ' storage array controller (raid) Ses = &HD ' enclosure services device Rbc = &HE ' simple direct access device Ocrw = &HF ' optical card reader Bcc = &H10 ' bridge controller Osd = &H11 ' object based storage device Adc = &H12 ' automation / drive interface WellKnown = &H1E Unknown = &H1F End Enum Sub Main() Console.WriteLine("Enter a drive letter") Dim letter As Char = Console.ReadKey.KeyChar Console.WriteLine() Const GenericRead As Integer = &H80000000 Const GenericWrite As Integer = &H40000000 Const FileShareRead As Integer = 1 Const FileShareWrite As Integer = 2 Const OpenExisting As Integer = 3 Dim drivePath As String = String.Concat("\\.\" & letter & ":") Console.WriteLine("Trying path: " & drivePath) Using driveHandle As SafeFileHandle = NativeMethods.CreateFile( _ drivePath, _ GenericRead Or GenericWrite, _ FileShareRead Or FileShareWrite, _ IntPtr.Zero, _ OpenExisting, _ 0, _ IntPtr.Zero) If driveHandle.IsInvalid Then Console.WriteLine("CreateFile ERROR: " & (New Win32Exception).Message) Console.ReadKey() Return End If Dim sptwb As New ScsiPassThroughWithBuffers sptwb.Init() Dim inBufferSize As Integer = Marshal.SizeOf(GetType(ScsiPassThroughWithBuffers)) Dim bytesReturned As Integer Const IOCTL_SCSI_PASS_THROUGH As Integer = &H4D004 Dim result As Boolean = NativeMethods.DeviceIoControl(driveHandle, IOCTL_SCSI_PASS_THROUGH, _ sptwb, inBufferSize, sptwb, inBufferSize, bytesReturned, IntPtr.Zero) If result = False Then Console.WriteLine("DeviceIOControl ERROR: {0} {1}", Marshal.GetLastWin32Error.ToString("x"), (New Win32Exception).Message) Console.ReadKey() Return End If Dim bytes() As Byte = sptwb.Data Dim peripheralQualifier As Integer = bytes(0) >> 5 If peripheralQualifier = 1 Then Console.WriteLine("No peripheral is attached to the device") Dim peripheralDevice As PeripheralDeviceType = CType(bytes(0) And &H1F, PeripheralDeviceType) Console.WriteLine("Peripheral device type: " & peripheralDevice.ToString) Console.WriteLine("Version of the standard:" & sptwb.Data(2)) Console.WriteLine("Additional Length: " & sptwb.Data(4)) DumpString("Vendor Id: ", sptwb.Data, 8, 8) DumpString("Product Id: ", sptwb.Data, 16, 16) DumpString("Product Revision Level: ", sptwb.Data, 32, 4) DumpString("Serial no: ", sptwb.Data, 36, 8) Console.WriteLine("Press any key") Console.ReadKey() End Using End Sub Private Sub DumpString(msg As String, bytes() As Byte, offset As Integer, length As Integer) Console.WriteLine(String.Format("{0} :'{1}'", msg, ASCIIEncoding.ASCII.GetString(bytes, offset, length))) End Sub End Module
The serial number is unreliable. The peripheral device type tells you which standard the device is using. I’m trying to work out how to get the serial no of my raided or USB drives. This command is getting back the serial number of my controller card for my raid array when I ask it for \\.\C:
Enter a drive letter
c
Trying path: \\.\c:
Peripheral device type: Sbc2
Version of the standard:4
Additional Length: 43
Vendor Id: :'AMD '
Product Id: :'2+0 Stripe/RAID0'
Product Revision Level: :'1.10'
Serial no: :'98031612'
Press any key