SD card Serial Number
-
Thursday, January 12, 2006 12:56 PM
Any body know about:
How can i get SD card Serial Number? (Pocket PC, Smartphone platforms)
All Replies
-
Thursday, January 26, 2006 7:46 PM
hy! Try Anton Tomov Pocket Mechanic v1.52 for PPC ;-)
-
Thursday, December 21, 2006 7:53 AMOK, so since it was done in Pocket Mechanic, it means it's possible to do.
Would anyone know how I could program it? (using Compact Framework, eVC++ or p/invoking a dll?)
I've been looking for it in many places, I haven't found anything.
Thanks in advance.
-
Thursday, December 21, 2006 11:44 PMModerator
The serial number sould be readable by issuing a IOCTL_DISK_GET_STORAGEID DeviceIoControl call.
See: http://msdn2.microsoft.com/en-us/library/ms901392.aspx
Here is a sample in C++: http://groups.google.com/group/microsoft.public.pocketpc.developer/browse_thread/thread/2cd5e40bfd753a62/f18769f4b8454299?lnk=st&q=IOCTL_DISK_GET_STORAGEID+c%23&rnum=1#f18769f4b8454299
-
Friday, December 22, 2006 7:26 AMOK, I don't have the opportunity to test it right now, but I'm going to keep this in my bookmarks so that I can use it when it's necessary. I'm trusting you it's working!

Thanks for your quick answer, Michael!
ADD: I marked your post as helpful.
-
Friday, December 22, 2006 10:32 PM
A bit more work was involved. I've used deviceIOControl quite alot from vb.net, so I figured I could get this going. Google didn't turn up any working .Net code.
Anyhoo, I think I have it working, someone who knows about these things can check it and tell me I'm doing it all wrong...
The main problem I had was getting the device name for the SD Card to pass to CreateFile. I've fudged it by reading all the valid ones from the registry. It also gets CF cards and god knows what else, which don't store serial nos and manufacturer ids. So if someone knows how to enumerate SD Cards and get thier device name (DSK1: etc) it would be great...edit, found another way, so the getdisks thing is overloaded now.
I don't know how useful this is though, it dosn't sound like it is a unique id.Option Strict On
Option Explicit On
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Public Class SDReader
#Region " Constants "
' Getting the const is fun:
' it equals CTL_CODE(IOCTL_DISK_BASE, 0x709, METHOD_BUFFERED, FILE_ANY_ACCESS)
' CTL_CODE (devtype, function, method, access) calculates:
' ((DeviceType) << 16) or ((Access) << 14) or ((Function) << 2) or (Method)
' which works out as this value when you calculate it.
' Why they can't just stick the values on the msdn I don't know. 8-)
Private Const ERROR_INSUFFICIENT_BUFFER As Integer = 122
Private Const ERROR_INVALID_NAME As Integer = 123
Private Const GenericRead As Integer = &H80000000
Private Const GenericWrite As Integer = &H40000000
Private Const FileAttributeNormal As Integer = &H80
Private Const FileShareRead As Integer = 1
Private Const FileShareWrite As Integer = 2
Private Const IOCTL_DISK_GET_STORAGEID As UInt32 = &H71C24
Private Const OpenExisting As Integer = 3
#End Region
#Region " P/Invoke "
' Send messages to devices.
<DllImport("coredll", SetLastError:=True)> _
Private Shared Function DeviceIoControl( _
ByVal deviceHandle As IntPtr, _
ByVal controlCode As Int32, _
ByVal inBuffer() As Byte, _
ByVal inBufferSize As Int32, _
<[In](), Out()> _
ByVal outBuffer() As Byte, _
ByVal outBufferSize As Int32, _
ByRef bytesReturned As Int32, _
ByRef overlapped As IntPtr) As Int32
End Function
' Used to get a handle on the drive.
<DllImport("coredll", SetLastError:=True)> _
Private Shared Function CreateFile( _
ByVal FileName As String, _
ByVal DesiredAccess As Int32, _
ByVal ShareMode As Int32, _
ByVal SecurityAttributes As Int32, _
ByVal CreationDisposition As Int32, _
ByVal FlagsAndAttributes As Int32, _
ByVal hTemplateFile As Integer) As IntPtr
End Function
' Close the handle.
<DllImport("coredll", SetLastError:=True)> _
Private Shared Function CloseHandle(ByVal hObject As IntPtr) As Integer
End Function
<DllImport("coredll", SetLastError:=True)> _
Private Shared Function GetDiskFreeSpaceEx( _
ByVal lpDirectoryName As String, _
ByRef FreeBytesAvailableToCaller As UInt64, _
ByRef TotalNumberOfBytes As UInt64, _
ByRef lpTotalNumberOfFreeBytes As UInt64) As Int32
End Function
#End Region
#Region " Structures "
' When deviceIOControl returns,
' this is at the head of the buffer, and has layout information.
Private Structure StorageID
Public dwSize As Int32
Public dwFlags As Int32
Public dwManufactureIDOffset As Int32
Public dwSerialNumOffset As Int32
' Read from the buffer and fill the fields
Public Sub FillFromBytes(ByVal buffer() As Byte)
' read the header from the bytes
dwSize = BitConverter.ToInt32(buffer, 0)
dwFlags = BitConverter.ToInt32(buffer, 4)
dwManufactureIDOffset = BitConverter.ToInt32(buffer, 8)
dwSerialNumOffset = BitConverter.ToInt32(buffer, 12)
End Sub
End Structure
''' <summary>
''' Information returned by DeviceIOControl.
''' </summary>
''' <remarks>See "Storage_Indentification" in the library.
''' Both fields are stored as strings.
''' For my cards, every pair of chars were reversed,
''' (Instead of 123456, it is stored 214365),
''' so I've re-reversed them.
''' </remarks>
Friend Class SDInfo
Public Serial As String
Public Manufacturer As String
Public Overrides Function ToString() As String
' blimey no Environment.NewLine urgh.
Return String.Format("Serial: {0}{1}Manufacturer: {2}", Serial, vbCrLf, Manufacturer)
End Function
End Class
#End Region
#Region " Private Methods "
' Get the handle
Private Shared Function GetDriveHandle(ByRef driveRoot As String) As IntPtr
If driveRoot.StartsWith("DSK") = False Then
driveRoot = driveRoot & "\\Vol:"
End If
Dim driveHandle As IntPtr = _
CreateFile(driveRoot, _
GenericRead, _
0, _
Nothing, _
OpenExisting, _
0, _
Nothing)
If driveHandle.ToInt32 = -1 Then
Dim errorCode As Integer = Marshal.GetLastWin32Error
If errorCode = ERROR_INVALID_NAME Then
' This happens with the built in memory in Dells.
Return IntPtr.Zero
End If
Throw New Exception(String.Format( _
"Couldn't CreateFile on {0}, recieved error: {1} &H{2}", _
driveRoot, errorCode, Convert.ToString(errorCode, 16)))
End If
Return driveHandle
End Function
Private Shared Function DecodeBuffer(ByVal buffer() As Byte) As SDInfo
Dim header As New StorageID
header.FillFromBytes(buffer)
Dim dskInfo As New SDInfo
Dim sb As New System.Text.StringBuilder
' urgh some sort of endian madness:
For i As Integer = (header.dwManufactureIDOffset + 1) To (header.dwSerialNumOffset - 1) Step 2
If buffer(i) > 0 Then sb.Append(Chr(buffer(i)))
If buffer(i - 1) > 0 Then sb.Append(Chr(buffer(i - 1)))
Next
dskInfo.Manufacturer = sb.ToString
sb = New System.Text.StringBuilder
For i As Integer = (header.dwSerialNumOffset + 1) To (header.dwSize - 1) Step 2
If buffer(i) > 0 Then sb.Append(Chr(buffer(i)))
If buffer(i - 1) > 0 Then sb.Append(Chr(buffer(i - 1)))
Next
dskInfo.Serial = sb.ToString
Return dskInfo
End Function
Private Shared Sub CloseFileHandle(ByVal driveHandle As IntPtr)
Dim result As Int32 = CloseHandle(driveHandle)
End Sub
' Fill the buffer.
' If the buffer is too small then return the errorcode!
Private Shared Function RequestStorageID(ByVal handle As IntPtr, ByRef buffer() As Byte) As Integer
' Send the buffer off to the device...
Dim bufferSize As Int32 = buffer.Length
Dim bytesReturned As Int32 = 0
Dim result As Int32 = _
DeviceIoControl(handle, _
IOCTL_DISK_GET_STORAGEID, _
buffer, _
bufferSize, _
buffer, _
bufferSize, _
bytesReturned, _
Nothing)
' We need to know when the buffer was not big enough.
' when this happens, result = 0 and marshal.GetLastWin32Error = ERROR_INSUFFICIENT_BUFFER
If result = 0 Then
Return Marshal.GetLastWin32Error
End If
Return result
End Function
#End Region
#Region " Friend Methods "
''' <summary>
''' Attempt to read the Serial from the device.
''' </summary>
''' <param name="dsk"></param>
''' <returns></returns>
''' <remarks>For SD Cards the device name is something like DSK1: DSK2:</remarks>
Friend Shared Function GetSerial(ByVal dsk As String) As SDInfo
Dim handle As IntPtr
Try
handle = GetDriveHandle(dsk)
Catch
Return Nothing
End Try
' This happens if the dsk string didn't work with CreateFile.
If handle = IntPtr.Zero Then Return Nothing
Try
' What size is the buffer?
' Well send off and get just the header, and read the size from that.
Dim buffer() As Byte = New Byte(Marshal.SizeOf(GetType(StorageID)) - 1) {}
If RequestStorageID(handle, buffer) = ERROR_INSUFFICIENT_BUFFER Then
' Ok, this is expected. We only filled in the header.
' Read the header and determine the correct size.
Dim header As New StorageID
header.FillFromBytes(buffer)
' Reset the buffer
buffer = New Byte(header.dwSize - 1) {}
' And ask for it again.
Dim result As Integer = RequestStorageID(handle, buffer)
If result = 0 Then
' It didn't work
Throw New Exception("DeviceIOControl returned " & Convert.ToString(result, 16))
End If
End If
If buffer.Length = 16 Then
' we only got the buffer. device has no info
Return Nothing
End If
' If we get here, then we have a buffer.
Return DecodeBuffer(buffer)
Finally
CloseHandle(handle)
End Try
End Function
''' <summary>
''' Get a list of DSK: devices from the registry.
''' </summary>
''' <returns></returns>
''' <remarks> TODO:
''' I'm getting them blindly - I've no idea which drive goes with
''' which dsk, and CF devices come back too.
''' In windows you can use getMSDosDeviceName.</remarks>
Friend Shared Function GetDrives() As List(Of String)
Dim lmDriversActive As RegistryKey = Registry.LocalMachine.OpenSubKey("Drivers\Active")
Dim dsks As New List(Of String)
Dim subKeyNames() As String = lmDriversActive.GetSubKeyNames
For subKeyIndex As Integer = 0 To lmDriversActive.SubKeyCount - 1
Dim name As String = _
DirectCast(lmDriversActive.OpenSubKey(subKeyNames(subKeyIndex)).GetValue("Name"), String)
If name IsNot Nothing AndAlso name.StartsWith("DSK") Then
dsks.Add(name)
End If
Next
Return dsks
End Function
Friend Shared Function GetDrives(ByVal minimumSizeMegabytes As UInt32) As List(Of String)
Dim cards As New List(Of String)
For Each di As IO.DirectoryInfo In (New IO.DirectoryInfo("\")).GetDirectories
If (di.Attributes And IO.FileAttributes.Directory) = IO.FileAttributes.Directory Then
If (di.Attributes And IO.FileAttributes.Temporary) = IO.FileAttributes.Temporary Then
' OpenNetCf technique.
' This is a directory and it is temporary.
' Dells have an irritating internal flash that is a false positive
Dim freeBytesAvailable As UInt64 = 0
Dim totalNumberOfBytes As UInt64 = 0
Dim totalNumberOfFreeBytes As UInt64 = 0
Dim result As Int32 = _
GetDiskFreeSpaceEx(di.FullName, _
freeBytesAvailable, _
totalNumberOfBytes, _
totalNumberOfFreeBytes)
If totalNumberOfBytes > (minimumSizeMegabytes * 1024 * 1024) Then
' more than 32 MB so prolly not the internal thing
cards.Add(di.FullName)
End If
End If
End If
Next
Return cards
End Function
#End Region
End Class
and the test:
Public Class Form1
Private label1 As New Label
Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
label1 = New Label
Me.Controls.Add(label1)
label1.Location = New Point(0, 0)
label1.Dock = DockStyle.Fill
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' drives bigger than 128MB
For Each dsk As String In SDReader.GetDrives(128)
Try
Dim sdInfo As SDReader.SDInfo = SDReader.GetSerial(dsk)
If sdInfo IsNot Nothing Then
label1.Text &= (SDReader.GetSerial(dsk).ToString)
label1.Text &= vbCrLf
End If
Catch ex As Exception
label1.Text = ex.Message
End Try
Next
End Sub
End Class
I'll put it on pscode.com (jo0ls) as the double spacing is a bit annoying...
CopyAsHtml -
Wednesday, December 27, 2006 7:37 AMThanks jo0ls for your message!

As I've said, I don't really have the possibility right now to do some tests & helping finding the best solution (& partially answer to your questions).
But I know where this thread is, & I'll come back on it as soon as I have the possibility to use it.
-
Wednesday, September 19, 2007 2:56 PM
Hi jo0ls
I come from China.
can u send this code(the project will be best ^_^ ) to me?thanks
and my MSN is rosyrain1121@hotmail.com
-
Saturday, November 29, 2008 1:58 PMPlease note the above code does not read the hardware serial number from the CID register of the card. Instead it reads a temporary serial number created by windows - not what people are looking for. IOCTL_DISK_GET_STORAGEID is not the way to go. Instead you can send IOCTL_SFFDISK_DEVICE_COMMAND with Cmd 10 from the simplified SD Spec. This will only work for cards plugged in to a reader connected directly to the PCI bus - it won't work with any USB card reader. It also depends on the SD Bus host driver playing ball and dealing with the request correctly. Also there seems to be some problem in Vista, with two hotfixes available for this particular command (kb 944240 948870). So, not too useful either I'm afraid. For usb readers people have said to use techniques like USBView.zip, but this gets a different number (and it gets the same number no matter which SD card you plug in). Presumably it is reading the serial number of the USB host controller.
Sorry for the gravedig, I can't edit my post as it is way over the max number of characters.

