I have some code to fetch a hard drive serial number from the WMI.
SelectQuery selectQuery = new SelectQuery("Win32_PhysicalMedia");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(selectQuery);
foreach (ManagementObject wmi_PM in searcher.Get())
{
string str = wmi_PM["SerialNumber"];
}
At first I thought it was working and retrieved the correct serial number. After trying to use it with a comparison though, I found out the number the WMI reports is not exactly correct. The WMI serial number is padded with a bunch of spaces as well as the characters are transposed.
The actual drive serial number printed on the sticker and returned by some tools (probably using DeviceIoControl) is "3RH8B1BG", WMI however returns " R38H1BGB".
Real Serial#: 3RH8B1BG
WMI Serial#: R38H1BGB
Some tools like SiSoftware Sandra, return this padded and transposed number, its not actual serial number though. The WMI value is the serial number if you transpose every other position. Is this normal? should I just code to transpose it to the correct value?
I try to avoid using the WMI but it seems any search for how to do something on the net now bring back WMI examples.
The WMI value serial number for 2 different hard drive of different manufactures are both transposed so its not a single disk.
Update: found some code using DeviceIoControl
http://addressof.com/blog/archive/2004/02/14/392.aspx
Surprisingly, DeviceIoControl returns a transposed serial number as well.
In the code by CorySmith above it has a SwapChars function
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
The c++ code he mentions has the flip to:
// 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)
{
...
}
guess that's standard for DeviceIoControl and WMI, cant believe any of the other solutions or examples i ran across did not have this.
Found a working Solution to decode the real HD-Serials. The following Link contains the code to decode even without admin-rights: Decoding Source
But if you get the Serials from the Win32_PhysicalMedia WMI class above Vista it may not work in all cases. Then you have to use the Win32_DiskDrive class (according to this Link: Jiliang Ge's Answer from Tuesday, October 27, 2009 3:12 AM
I added the code (in VB, since I usually code in VB.NET). I didn't want to steal someone else's Code. I included as much Infos and still some links to the Original Coder within the code. It now also includes decoding Serialnumbers from Removable Drives (in the same routine).
Hope it helps.
''' <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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With