转换WMI HDD序列号

时间:2012-06-12 00:58:53

标签: c# wmi transpose serial-number

我有一些代码可以从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”。

Real Serial#:3RH8B1BG
WMI Serial#: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的标准,不能相信我遇到的任何其他解决方案或示例都没有这个。

1 个答案:

答案 0 :(得分:1)

找到一个解决真实HD-Serials的工作解决方案。以下链接包含即使没有管理员权限也要解码的代码:Decoding Source

但是如果你从Vista上面的Win32_PhysicalMedia WMI类获得Serials,它可能无法在所有情况下使用。然后你必须使用Win32_DiskDrive类(根据此链接:Jiliang Ge's Answer from Tuesday, October 27, 2009 3:12 AM

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

希望它有所帮助。

   ''' <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