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

    hy! Try Anton Tomov Pocket Mechanic v1.52  for PPC  ;-)

  • Thursday, December 21, 2006 7:53 AM
     
     
    OK, 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 PM
    Moderator
     
     Answered
  • Friday, December 22, 2006 7:26 AM
     
     
    OK, 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 AM
     
     
    Thanks 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 PM
     
     
    Please 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.