Any body know about:

 How can i get SD card Serial Number (Pocket PC, Smartphone platforms)   

Re: .NET Compact Framework SD card Serial Number

Access Denied

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

Re: .NET Compact Framework SD card Serial Number

Le Sage

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.

Re: .NET Compact Framework SD card Serial Number

Re: .NET Compact Framework SD card Serial Number

Le Sage

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. 

Re: .NET Compact Framework SD card Serial Number


A bit more work was involved. I've used deviceIOControl quite alot from, 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:


    ' 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, _


        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


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


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


        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, _



        ' 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


            handle = GetDriveHandle(dsk)


            Return Nothing

        End Try


        ' This happens if the dsk string didn't work with CreateFile.

        If handle = IntPtr.Zero Then Return Nothing



            ' 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


                ' 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)




        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


            End If


        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, _



                    If totalNumberOfBytes > (minimumSizeMegabytes * 1024 * 1024) Then

                        ' more than 32 MB so prolly not the internal thing


                    End If

                End If

            End If


        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.



        ' Add any initialization after the InitializeComponent() call.

        label1 = New Label


        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)


                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



    End Sub


End Class

I'll put it on (jo0ls) as the double spacing is a bit annoying...


Re: .NET Compact Framework SD card Serial Number

Le Sage

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.

Re: .NET Compact Framework SD card Serial Number

Eric Liang

Hi jo0ls

I come from China.

can u send this code(the project will be best ^_^ ) to me thanks

and my MSN is