Here is what I found on the net. It works on my Moto Q WinMO 6.1. I know this post is a year old but I still have many issues finding anything that is easy involving SD cards.
Imports Microsoft.WindowsMobile
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports Microsoft.VisualBasic
Imports System.Reflection
Public Class Form1
' <DllImport("coredll.dll")> _
'Private Shared Function KernelIoControl(ByVal IoControlCode As Int32, ByVal InputBuffer As IntPtr, ByVal InputBufferSize As Int32, ByVal OutputBuffer As Byte(), ByVal OutputBufferSize As Int32, ByRef BytesReturned As Int32) As Boolean
' End Function
' Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Const IOCTL_DISK_GET_STORAGEID As UInt32 = &H71C24
' Dim OutputBuffer As Byte() = New Byte(255) {}
' Dim OutputBufferSize As UInteger = OutputBuffer.Length
' Dim BytesReturned As UInteger = 0
' KernelIoControl(IOCTL_DISK_GET_STORAGEID, IntPtr.Zero, 0, OutputBuffer, OutputBufferSize, BytesReturned)
' End Sub
' '
' ' [DllImport("coredll.dll")]
' ' private static extern bool KernelIoControl(Int32 IoControlCode, IntPtr
' ' InputBuffer, Int32 InputBufferSize, byte[] OutputBuffer, Int32
' ' OutputBufferSize, ref Int32 BytesReturned);
' '
' ' byte[] OutputBuffer = new byte[256];
' ' uint OutputBufferSize = OutputBuffer.Length;
' ' uint bre=0;
' ' KernelIoControl(IOCTL_DISK_GET_STORAGEID,IntPtr.Zero,0,OutputBuffer,OutputBufferSize,ref
' 'BytesReturned);
' '
' 'funny part : I don't have evc++4.0, so if someone can send me/followup VALUE
' 'of IOCTL_DISK_GET_STORAGEID (or heder file containing same), thanks
' 'shi...@softhome.net
' Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' End Sub
Private Sub Form1_Load(ByVal sender As System.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
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-)
'12345678_7068_42EF_8459_75584C1CDB8F
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_GETINFO As UInt32 =
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
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 "
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
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 System.IO.DirectoryInfo In (New System.IO.DirectoryInfo("\")).GetDirectories
If (di.Attributes And System.IO.FileAttributes.Directory) = System.IO.FileAttributes.Directory Then
If (di.Attributes And System.IO.FileAttributes.Temporary) = System.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
|