Get HDD (and NOT Volume) Serial Number on Vista Ultimate 64 bit

后端 未结 7 1295
忘掉有多难
忘掉有多难 2020-12-28 10:05

I was once looking for getting the HDD serial number without using WMI, and I found it. The code I found and posted on StackOverFlow.com works very well on 32 bit Windows, b

7条回答
  •  萌比男神i
    2020-12-28 10:52

    You need to ensure that your P/Invoke definitions are 64-bit friendly. Alternatively, try setting the target CPU of the projects in your solution to 32-bit. More information on P/Invoke and 64-bit can be found here.

    EDIT:

    The following rewritten code might work better for you - basically I've tidied up the P/Invoke definitions and added better error handling. The code makes two attempts to obtain the serial number. The first uses IOCTL_STORAGE_QUERY_PROPERTY and the second uses SMART_RCV_DRIVE_DATA.

    ' 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"
         _
        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
    
         _
        Private Structure SENDCMDINPARAMS
            Public cBufferSize As Int32
            Public irDriveRegs As IDEREGS
            Public bDriveNumber As Byte
             _
            Public bReserved As Byte()
             _
            Public dwReserved As Int32()
             _
            Public bBuffer As Byte()
        End Structure
    
         _
        Private Structure DRIVERSTATUS
            Public bDriverError As Byte
            Public bIDEError As Byte
             _
            Public bReserved As Byte()
             _
            Public dwReserved As Int32()
        End Structure
    
         _
        Private Structure SENDCMDOUTPARAMS
            Public cBufferSize As Int32
            Public DriverStatus As DRIVERSTATUS
             _
            Public bBuffer As Byte()
        End Structure
    
         _
        Private Structure GETVERSIONOUTPARAMS
            Public bVersion As Byte
            Public bRevision As Byte
            Public bReserved As Byte
            Public bIDEDeviceMap As Byte
            Public fCapabilities As Int32
             _
            Public dwReserved As Int32()
        End Structure
    
         _
        Private Structure STORAGE_PROPERTY_QUERY
            Public PropertyId As Int32
            Public QueryType As Int32
             _
            Public AdditionalParameters As Byte()
        End Structure
    
         _
        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
             _
            Public RawDeviceProperties As Byte()
        End Structure
    
         _
        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
    
         _
        Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In](), Out()> ByRef lpInBuffer As SENDCMDINPARAMS, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As SENDCMDOUTPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32
        End Function
    
         _
        Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, ByVal lpInBuffer As IntPtr, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As GETVERSIONOUTPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32
        End Function
    
         _
        Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In](), Out()> ByRef lpInBuffer As STORAGE_PROPERTY_QUERY, ByVal nInBufferSize As Int32, <[In](), Out()> ByRef lpOutBuffer As STORAGE_DEVICE_DESCRIPTOR, 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 = &H80000000
        Private Const GENERIC_WRITE As Int32 = &H40000000
        Private Const FILE_SHARE_READ As Int32 = &H1
        Private Const FILE_SHARE_WRITE As Int32 = &H2
        Private Const FILE_SHARE_DELETE As Int32 = &H4
        Private Const SMART_GET_VERSION As Int32 = &H74080
        Private Const SMART_RCV_DRIVE_DATA As Int32 = &H7C088
        Private Const ID_CMD As Int32 = &HEC
        Private Const IDENTIFY_BUFFER_SIZE As Int32 = 512
        Private Const CAP_SMART_CMD As Int32 = &H4
        Private Const IOCTL_STORAGE_QUERY_PROPERTY As Int32 = &H2D1400
        Private Const PropertyStandardQuery As Int32 = 0
        Private Const StorageDeviceProperty As Int32 = 0
    #End Region
    
        Public Shared Function GetSerialNumber(ByVal diskNumber As Integer) As String
            Dim result As String = GetSerialNumberUsingStorageQuery(diskNumber)
            If String.IsNullOrEmpty(result) Then
                result = GetSerialNumberUsingSmart(diskNumber)
            End If
            Return result
        End Function
    
        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 > 0 Then
                    Dim rawDevicePropertiesOffset As Integer = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length
                    Dim pos As Integer = sdd.SerialNumberOffset - rawDevicePropertiesOffset
                    While pos < iBytesReturned And sdd.RawDeviceProperties(pos) <> 0
                        result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1))
                        pos += 1
                    End While
                End If
                Return result.ToString()
            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()
                Else
                    Return String.Empty
                End If
            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 gvo As New GETVERSIONOUTPARAMS
            If DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, gvo, Marshal.SizeOf(gvo), iBytesReturned, 0) = 0 Then
                Return False
            End If
            Return (gvo.fCapabilities And CAP_SMART_CMD) > 0
        End Function
    
    End Class
    

    This is the code to call it:

    ' 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} - serial number: [{1}]", drive, PhysicalDrive.GetSerialNumber(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
    

    I only have one 64-bit machine to test against, but this code does work on it.

提交回复
热议问题