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 値は、1 つおきに転置した場合のシリアル番号です。これは正常ですか?それを正しい値に転置するようにコーディングする必要がありますか?

私は 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 シリアルをデコードするための有効なソリューションを見つけました。次のリンクには、管理者権限がなくてもデコードするコードが含まれています:ソースのデコード

ただし、Vista より上の Win32_PhysicalMedia WMI クラスからシリアルを取得すると、すべての場合に機能するとは限りません。次に、Win32_DiskDrive クラスを使用する必要があります (このリンクによると: Jiliang Ge's Answer from Tuesday, October 27, 2009 3:12 AM

コードを追加しました (通常は VB.NET でコーディングするため、VB で)。他人のコードを盗むつもりはありませんでした。コード内にできるだけ多くの情報を含め、元のコーダーへのリンクをいくつか含めました。また、リムーバブル ドライブからのシリアル番号のデコードも含まれるようになりました (同じルーチン内)。

それが役に立てば幸い。

   ''' <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 に答える