WMI硬盘序列号转置

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示例。
两个不同制造商的硬盘的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来说是标准的,无法相信我遇到的其他解决方案或示例没有这个。


2
哇,看起来你正在运行PDP-11 - sarnold
4
实际上,他正在使用具有与PDP-11相同的小端字节顺序的计算机。我也是。如果你正在运行Windows(不是Windows CE和Windows Phone),那么你也是。唯一可行的解决方案是调用DeviceIoControl并自己翻转字节,就像原帖作者发现的那样。如果你调用WMI,你不知道WMI是否已经为你翻转了字节。 - Windows programmer
1个回答

1

我找到了一种有效的解码真实HD序列号的方法。下面的链接包含一个解码代码,即使你没有管理员权限也可以使用:Decoding Source

但是,如果你从Win32_PhysicalMedia WMI类中获取序列号(在Vista以上版本),可能不适用于所有情况。此时,你需要使用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

1
仅仅发布链接对任何人都没有好处。你需要在回答中包含相关的信息。 - Sam Axe
对不起,我只是不想从别人那里抄袭代码。 - dragonfly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接