6

我有一些代码可以从 WMI 获取硬盘序列号。

SelectQuery selectQuery = new SelectQuery("Win32_PhysicalMedia");
ManagementObjectSearcher searcher =
             new ManagementObjectSearcher(selectQuery);
foreach (ManagementObject wmi_PM in searcher.Get())
{
      string str = wmi_PM["SerialNumber"];
}

起初我认为它正在工作并检索到正确的序列号。在尝试使用它进行比较之后,我发现 WMI 报告的数字并不完全正确。WMI 序列号用一堆空格填充,并且字符被调换。

打印在贴纸上并由某些工具(可能使用 DeviceIoControl)返回的实际驱动器序列号是“3RH8B1BG”,但 WMI 返回“R38H1BGB”。

真实序列号:3RH8B1BG
WMI 序列号:R38H1BGB

一些工具,如 SiSoftware Sandra,返回这个填充和转置的数字,但它不是实际的序列号。如果您每隔一个位置调换一次,WMI 值就是序列号。这是正常的吗?我应该只编码以将其转换为正确的值吗?

我尽量避免使用 WMI,但现在似乎在网上搜索如何做某事都会带回 WMI 示例。

不同制造商的2个不同硬盘驱动器的WMI值序列号都被调换了,因此它不是一个磁盘。



更新:使用 DeviceIoControl 找到了一些代码

     http://addressof.com/blog/archive/2004/02/14/392.aspx

令人惊讶的是,DeviceIoControl 也返回了一个转置的序列号。在上面 CorySmith 的代码中,它有一个 SwapChars 函数

Private Shared Function SwapChars(ByVal chars() As Char) As String
  For i As Integer = 0 To chars.Length - 2 Step 2
    chars.Reverse(chars, i, 2)
  Next
  Return New String(chars).Trim
End Function

他提到的 c++ 代码有翻转到:

    //  function to decode the serial numbers of IDE hard drives
    //  using the IOCTL_STORAGE_QUERY_PROPERTY command 
char * flipAndCodeBytes (const char * str,
             int pos,
             int flip,
             char * buf)
{
    ...
}

猜这是 DeviceIoControl 和 WMI 的标准,不敢相信我遇到的任何其他解决方案或示例都没有这个。

4

1 回答 1

1

找到了一个可行的解决方案来解码真正的 HD-Serials。以下链接包含即使没有管理员权限也可以解码的代码:解码源

但是,如果您从 Vista 之上的 Win32_PhysicalMedia WMI 类中获得 Serials,则它可能并非在所有情况下都有效。然后你必须使用 Win32_DiskDrive 类(根据这个链接:Jiliang Ge's Answer from Tuesday, October 27, 2009 3:12 AM

我添加了代码(在 VB 中,因为我通常在 VB.NET 中编写代码)。我不想窃取别人的代码。我在代码中包含了尽可能多的信息和一些指向原始编码器的链接。它现在还包括从可移动驱动器中解码序列号(在同一例程中)。

希望能帮助到你。

   ''' <summary>
''' Decode Manufacuter Disk Serialnumbers (also for PNP USB-Drives)
''' </summary>
''' <param name="InterfaceType">InterfaceType from Win32_DiskDrive WMI-Class</param>
''' <param name="PNPDeviceID">PNPDeviceID from Win32_DiskDrive WMI-Class</param>
''' <param name="strVolumeSerial">Raw Serialnumber to be decoded</param>
''' <returns>Decoded Serialnumber</returns>
''' <remarks></remarks>
Public Shared Function Decode_HD_Serial(ByVal InterfaceType As String,
                          ByVal PNPDeviceID As String,
                          ByVal strVolumeSerial As String) As String

    'HANDLE USB PNP Devices differently (Removable USB-Sticks)
    'see: http://www.experts-exchange.com/Programming/Languages/.NET/Q_24574066.html

    If InterfaceType = "USB" Then
        Dim splitDeviceId As String() = PNPDeviceID.Split("\"c)
        Dim arrayLen As Integer = splitDeviceId.Length - 1
        Dim serialArray As String() = splitDeviceId(arrayLen).Split("&"c)
        Return serialArray(0)
    Else
        'Link:https://social.msdn.microsoft.com/Forums/vstudio/en-US/8523d7b9-0dc8-4d87-be69-a482aec9ee5e/wmi-win32physicalmedia-smart-id-in-vista-and-7-permissions?forum=netfxbcl
        'After digging into the [Win32_PhysicalMedia] WMI class, I find that from Vista/Longhorn the 
        'class has been taken over by another class called [Win32_DiskDrive]. Thus, if all machines 
        'in your environment are Vista and above use the second class otherwise use the first one. 
        'Based on my tests, the class gives the unique form of serial number when you run the 
        'app as an admin or as a non-admin. 
        ' ---> IF System.Environment.OSVersion.Version.Major > 5 then its Vista or higher. USE WIN32_DiskDrive

        Dim strVolumeSerialDecoded As String = String.Empty
        'Remove all space characters ("20").
        'Example : 20202020205635424544434553 will be 5635424544434553.
        strVolumeSerial.Trim.Replace("20", "")
        'IF THE USER IS ADMINISTRATOR, THE strVolumeSerial STRING WILL ALREADY CONTAIN THE SERIAL NUMBER IN ASCII, AND NO CONVERSION IS REQUIRED (Microsoft bug ?),
        'BUT IF THE strVolumeSerial STRING IS A HEX STRING, CONVERT IT TO ASCII :
        If System.Text.RegularExpressions.Regex.IsMatch(strVolumeSerial, "^[a-fA-F0-9]+$") Then
            'Convert to ASCII. Example : 5635424544434553 will be converted to V5BEDCES.
            strVolumeSerial = HexDecode(strVolumeSerial)
            'Swap pairs of characters.
            'Example : V5BEDCES will be converted to 5VEBCDSE.
            Dim serialNumber2 As String = ""
            For i As Integer = 0 To strVolumeSerial.Length - 1 Step 2
                strVolumeSerialDecoded &= strVolumeSerial(i + 1)
                strVolumeSerialDecoded &= strVolumeSerial(i)
            Next
            'Return the serialnumber as ASCII string.
            Return strVolumeSerialDecoded.Trim
        Else 'If strVolumeSerial is ASCII, remove spaces and return the serialnumber string.
            Return strVolumeSerial.Trim
        End If
    End If
End Function

''' <summary>Decodes a HEX-string to an ASCII string.</summary>
''' <param name="strHEX">The HEX-string to decode.</param>
''' <returns>If succeeded, the decoded String, an empty String if failed.</returns>
Private Shared Function HexDecode(ByVal strHEX As String) As String
    Try
        Dim sb As StringBuilder = New StringBuilder
        For i As Integer = 0 To strHEX.Length - 1 Step 2
            sb.Append(Convert.ToChar(Convert.ToUInt32(strHEX.Substring(i, 2), 16)).ToString)
        Next
        Return sb.ToString
    Catch ex As Exception
        Return ""
    End Try
End Function
于 2015-03-08T13:16:40.597 回答