【问题标题】:Get HDD (and NOT Volume) Serial Number on Vista Ultimate 64 bit在 Vista Ultimate 64 位上获取 HDD(而非卷)序列号
【发布时间】:2010-10-21 09:21:18
【问题描述】:

我曾经想在不使用 WMI 的情况下获取 HDD 序列号,结果找到了。 The code I found and posted on StackOverFlow.com 在 XP 和 Vista 的 32 位 Windows 上运行良好。当我尝试在 64 位操作系统(特别是 Vista Ultimate 64)上获取序列号时,问题才开始。该代码始终返回 String.Empty 或 Space。

有人知道如何解决这个问题吗?

编辑:

我使用了 Dave Cluderay 建议的工具,结果很有趣:

这是 DiskId32 在 Windows XP SP2 32 位上的输出:

To get all details use "diskid32 /d"
Trying to read the drive IDs using physical access with admin rights
Drive 0 - Primary Controller -  - Master drive
Drive Model Number________________: [MAXTOR STM3160215AS]
Drive Serial Number_______________: [            6RA26XK3]
Drive Controller Revision Number__: [3.AAD]
Controller Buffer Size on Drive___: 2097152 bytes
Drive Type________________________: Fixed
Drive Size________________________: 160041885696 bytes

Trying to read the drive IDs using the SCSI back door

Drive 4 - Tertiary Controller -  - Master drive
Drive Model Number________________: [MAXTOR STM3160215AS]
Drive Serial Number_______________: [            6RA26XK3]
Drive Controller Revision Number__: [3.AAD]
Controller Buffer Size on Drive___: 2097152 bytes
Drive Type________________________: Fixed
Drive Size________________________: 160041885696 bytes

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

**** STORAGE_DEVICE_DESCRIPTOR for drive 0 ****
Vendor Id = []
Product Id = [MAXTOR STM3160215AS]
Product Revision = [3.AAD]
Serial Number = []

**** DISK_GEOMETRY_EX for drive 0 ****
Disk is fixed
DiskSize = 160041885696

Trying to read the drive IDs using Smart

Drive 0 - Primary Controller -  - Master drive

Drive Model Number________________: [MAXTOR STM3160215AS]
Drive Serial Number_______________: [            6RA26XK3]
Drive Controller Revision Number__: [3.AAD]
Controller Buffer Size on Drive___: 2097152 bytes
Drive Type________________________: Fixed
Drive Size________________________: 160041885696 bytes

Hard Drive Serial Number__________:             6RA26XK3

Hard Drive Model Number___________: MAXTOR STM3160215AS

并且 DiskId32 在 Windows Vista Ultimate 64 位上运行:

To get all details use "diskid32 /d"

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

Trying to read the drive IDs using the SCSI back door

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

**** STORAGE_DEVICE_DESCRIPTOR for drive 0 ****
Vendor Id = [MAXTOR S]
Product Id = [TM3160215AS]
Product Revision = [3.AA]
Serial Number = []

**** DISK_GEOMETRY_EX for drive 0 ****
Disk is fixed
DiskSize = 160041885696

Trying to read the drive IDs using Smart

Hard Drive Serial Number__________:

Hard Drive Model Number___________:

请注意 Vista 上的信息少了多少,以及如何不返回序列号。另一个工具 EnumDisk 将我在 Vista 上的硬盘称为“SCSI”,而不是 Windows XP 上的“ATA”。

有什么想法吗?

编辑 2:

我正在发布来自 EnumDisks 的结果:

在 Windows XP SP2 32 位上:

Properties for Device 1

Device ID: IDE\DiskMAXTOR_STM3160215AS_____________________3.AAD___

Adapter Properties
------------------
Bus Type       : ATA
Max. Tr. Length: 0x20000
Max. Phy. Pages: 0xffffffff
Alignment Mask : 0x1

Device Properties
-----------------
Device Type     : Direct Access Device (0x0)
Removable Media : No
Product ID      : MAXTOR STM3160215AS
Product Revision: 3.AAD

Inquiry Data from Pass Through
------------------------------
Device Type: Direct Access Device (0x0)
Vendor ID  : MAXTOR S
Product ID : TM3160215AS
Product Rev: 3.AA
Vendor Str :



***  End of Device List  ***

在 Vista 64 Ultimate 上:

Properties for Device 1

Device ID: SCSI\DiskMAXTOR_STM3160215AS_____3.AA

Adapter Properties
------------------
Bus Type       : FIBRE
Max. Tr. Length: 0x20000
Max. Phy. Pages: 0x11
Alignment Mask : 0x0

Device Properties
-----------------
Device Type     : Direct Access Device (0x0)
Removable Media : No
Vendor ID       : MAXTOR S
Product ID      : TM3160215AS
Product Revision: 3.AA

Inquiry Data from Pass Through
------------------------------
Device Type: Direct Access Device (0x0)
Vendor ID  : MAXTOR S
Product ID : TM3160215AS
Product Rev: 3.AA
Vendor Str :



***  End of Device List  ***

【问题讨论】:

  • 更多可能相关的信息——这两个操作系统是在同一台物理机器上(即双启动)还是不同的机器上?要么是虚拟机吗?关于磁盘的任何值得注意的事情,例如(RAID 等)?
  • 两个操作系统都在同一台物理机器上。没有使用虚拟机。我的硬盘没有什么特别之处,一个典型的 ATA MAXTOR 160 GB 硬盘。
  • 你好。想必EnumDisk1在Vista 64上也获取不到序列号?
  • (另外,EnumDisk1有没有输出错误信息?)
  • 据我记忆,没有错误。我在这里粘贴了 EnumDisk1 的整个输出。

标签: windows-vista serial-number hdd


【解决方案1】:

此代码尝试获取序列号 3 次:

  1. 使用IOCTL_STORAGE_QUERY_PROPERTY
  2. 使用SMART_RCV_DRIVE_DATA
  3. 使用IOCTL_SCSI_PASS_THROUGH

此代码适用于 64 位:

' 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"
    <StructLayout(LayoutKind.Sequential)> _
    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

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure SENDCMDINPARAMS
        Public cBufferSize As Int32
        Public irDriveRegs As IDEREGS
        Public bDriveNumber As Byte
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)> _
        Public bReserved As Byte()
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _
        Public dwReserved As Int32()
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _
        Public bBuffer As Byte()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure DRIVERSTATUS
        Public bDriverError As Byte
        Public bIDEError As Byte
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _
        Public bReserved As Byte()
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _
        Public dwReserved As Int32()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure SENDCMDOUTPARAMS
        Public cBufferSize As Int32
        Public DriverStatus As DRIVERSTATUS
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=IDENTIFY_BUFFER_SIZE)> _
        Public bBuffer As Byte()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure GETVERSIONINPARAMS
        Public bVersion As Byte
        Public bRevision As Byte
        Public bReserved As Byte
        Public bIDEDeviceMap As Byte
        Public fCapabilities As Int32
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _
        Public dwReserved As Int32()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure STORAGE_PROPERTY_QUERY
        Public PropertyId As Int32
        Public QueryType As Int32
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _
        Public AdditionalParameters As Byte()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    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
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=10240)> _
        Public RawDeviceProperties As Byte()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    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
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> _
        Public Cdb As Byte()
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure SCSI_PASS_THROUGH_WITH_BUFFER
        Public Spt As SCSI_PASS_THROUGH
        Public Filler As Int32
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=64)> _
        Public Buffer As Byte()
    End Structure

    <DllImport("kernel32.dll", SetLastError:=True)> _
    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

    <DllImport("kernel32.dll", SetLastError:=True)> _
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In]()> 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

    <DllImport("kernel32.dll", SetLastError:=True)> _
    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 GETVERSIONINPARAMS, ByVal nOutBufferSize As Int32, ByRef lpBytesReturned As Int32, ByVal lpOverlapped As Int32) As Int32
    End Function

    <DllImport("kernel32.dll", SetLastError:=True)> _
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In]()> 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

    <DllImport("kernel32.dll", SetLastError:=True)> _
    Private Shared Function DeviceIoControl(ByVal hDevice As SafeFileHandle, ByVal dwIoControlCode As Int32, <[In]()> ByRef lpInBuffer As SCSI_PASS_THROUGH_WITH_BUFFER, ByVal nInBufferSize As Int32, <[In](), Out()> 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 = &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 IOCTL_SCSI_PASS_THROUGH As Int32 = &H4D004
    Private Const SCSI_IOCTL_DATA_IN As Int32 = &H1
    Private Const PropertyStandardQuery As Int32 = 0
    Private Const StorageDeviceProperty As Int32 = 0
    Private Const ERROR_INVALID_FUNCTION As Int32 = &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 > 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().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) = &H12 ' INQUIRY
            cdb(1) = &H1 ' EVPD bit
            cdb(2) = &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 <> 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 < spt.Spt.DataTransferLength And spt.Buffer(pos) <> 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) > 0
    End Function

End Class

这是调用它的代码:

' 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

EDIT - 我更改了主要方法以显示每次尝试的结果以进行比较。这有望说明这些技术的成功与否。

【讨论】:

  • 嗨!我将您的代码转换为 C#,但使用 SCSI PassThrough 我得到了一个奇怪的结果(型号而不是序列号)。我在这里发布了一个关于此的问题,如果您可以查看它,我会很高兴,因为您可能知道发生了什么:stackoverflow.com/questions/16443215/…
【解决方案2】:

将 DeviceIoControl 与 IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER 一起使用

或查看IOCTL_CHANGER_GET_PRODUCT_DATA

【讨论】:

  • IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER 用于 USB 设备
【解决方案3】:

This code 应该给你硬盘序列号。它与您链接的代码相似 (ReadPhysicalDriveInNTWithAdminRights),但有几个附加功能。

【讨论】:

    【解决方案4】:

    您需要确保您的 P/Invoke 定义是 64 位友好的。或者,尝试将解决方案中项目的目标 CPU 设置为 32 位。有关 P/Invoke 和 64 位的更多信息,请参阅here

    编辑:

    以下重写的代码可能更适合您 - 基本上我已经整理了 P/Invoke 定义并添加了更好的错误处理。该代码两次尝试获取序列号。第一个使用IOCTL_STORAGE_QUERY_PROPERTY,第二个使用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"
        <StructLayout(LayoutKind.Sequential)> _
        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
    
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure SENDCMDINPARAMS
            Public cBufferSize As Int32
            Public irDriveRegs As IDEREGS
            Public bDriveNumber As Byte
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)> _
            Public bReserved As Byte()
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _
            Public dwReserved As Int32()
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _
            Public bBuffer As Byte()
        End Structure
    
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure DRIVERSTATUS
            Public bDriverError As Byte
            Public bIDEError As Byte
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _
            Public bReserved As Byte()
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> _
            Public dwReserved As Int32()
        End Structure
    
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure SENDCMDOUTPARAMS
            Public cBufferSize As Int32
            Public DriverStatus As DRIVERSTATUS
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=IDENTIFY_BUFFER_SIZE)> _
            Public bBuffer As Byte()
        End Structure
    
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure GETVERSIONOUTPARAMS
            Public bVersion As Byte
            Public bRevision As Byte
            Public bReserved As Byte
            Public bIDEDeviceMap As Byte
            Public fCapabilities As Int32
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> _
            Public dwReserved As Int32()
        End Structure
    
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure STORAGE_PROPERTY_QUERY
            Public PropertyId As Int32
            Public QueryType As Int32
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)> _
            Public AdditionalParameters As Byte()
        End Structure
    
        <StructLayout(LayoutKind.Sequential)> _
        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
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=10240)> _
            Public RawDeviceProperties As Byte()
        End Structure
    
        <DllImport("kernel32.dll", SetLastError:=True)> _
        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
    
        <DllImport("kernel32.dll", SetLastError:=True)> _
        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
    
        <DllImport("kernel32.dll", SetLastError:=True)> _
        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
    
        <DllImport("kernel32.dll", SetLastError:=True)> _
        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
    

    这是调用它的代码:

    ' 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
    

    我只有一台 64 位机器可供测试,但这段代码确实可以运行。

    【讨论】:

    • 64 位 Vista?因为我在我的 64 位 Vista Ultimate 上测试了这段代码,它仍然返回一个空字符串。 (我在结果上使用 String.Trim,因为在 32 位操作系统上的实际序列号之前返回了额外的空格字符)似乎没有 .NET 代码能够返回 SN。我怎样才能联系到 MS 的程序员来讨论这个问题?
    • 我在 Windows 7 RC x64 HP ML-115 服务器上测试了这段代码。有一些可用的 C++ 示例,您可以以编译后的可执行形式下载这些示例。我特别想到的示例是 diskid32 (winsim.com/diskid32/diskid32.html) 和 EnumDisk1 (support.microsoft.com/kb/264203)。我建议您尝试在 64 位机器上运行可执行文件。如果他们能够检索到序列号,也许我或其他人可以帮助您在自己的 VB.net 代码中实现相同的技术。
    【解决方案5】:

    在 Windows 7 64 上工作:

            ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
    
            foreach (ManagementObject obj in mos.Get()) {
                Trace.TraceInformation("Information about disk drive {0}:", obj["Name"]);
                Trace.Indent();
                foreach (PropertyData pd in obj.Properties)
                    Trace.TraceInformation("Name \"{0}\": \"{1}\"", pd.Name, pd.Value);
                Trace.Unindent();
    
                obj.Properties["SerialNumber"]
            }
    

    可能 Win32_PhysicalMedia 类不在 64 位平台上提供。

    此时,即使是 Disk32 也可以正常工作(除了翻转序列号字节时的错误),因为基于相同的概念。

    【讨论】:

    • 我刚刚在我的机器(XP,使用 .NET 4.0)上尝试了这段代码,访问 obj.Properties["SeraliNumber"] 抛出“未找到”异常。
    • 我刚刚在我的机器(XP,使用 .NET 4.0)上尝试了这段代码,访问 obj.Properties["SeraliNumber"] 抛出“未找到”异常。
    • 在 Windows XP 上,您应该查询 Win32_PhysicalMedia。此代码适用于 Windows 7。
    【解决方案6】:

    修改自代码here:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Management;
    using System.Text;
    
    namespace Console_DiskDrive
    {
        class Program
        {
            static void Main(string[] args)
            {
                String query = "SELECT * FROM Win32_DiskDrive";
    
                foreach (ManagementObject item in new ManagementObjectSearcher(query).Get())
                {
                    string serialNumber = Convert.ToString(item["SerialNumber"]);
    
                    Console.WriteLine(serialNumber);
                }
    
                Console.ReadLine();
            }
        }
    }
    

    在我运行 Vista Home Premium x64 的系统上,给我一个 40 个字符的十六进制字符串,我假设它是我的序列号。稍后我会打开盒子确认一下,但请尝试一下,看看它是否是您要查找的内容。

    【讨论】:

    • 我之前测试过。获取硬盘序列号的 WMI 存在一些问题,包括无法在某些版本的 Windows 上工作以及间歇性故障,所以这不是答案。
    • 在我的笔记本电脑(运行 XP 的 DELL Latitude E6400)上,访问“SerialNumber”属性会引发“未找到”异常。
    【解决方案7】:

    您可能希望使用 Windows 非托管 API 来执行此操作:

    使用适当的结构调用GetVolumeInformation api 并找到 VolumeSerialNumber 整数字段。

    这个 API 已经存在了很长时间,并且从 Windows 98 开始就为我工作。不幸的是,无法在 x64 上检查它。

    您能否使用其他 Windows 工具查看正确的序列号? 顺便说一句:“0”是一个有效的序列号!如果磁盘映像是从备份或类似的东西恢复的,则可能会发生这种情况。

    【讨论】:

    • 感谢您的回复,但我不是在找Volume序列号,而是硬盘的序列号、型号等。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多