Question

Is there an easy way to find the storage card's path on a Windows Mobile device when there is a storage card and a bluetooth ftp connection?

Was it helpful?

Solution

Keep in mind that "\Storage Card" is english oriented. A device made for a different region may have a different name. The name of the storage card path on my device varies with how I am using the device.

Some time ago in the MSDN forms I responded to a few questions on how to detect the storage cards in the file system and how does one get the storage card's capacity. I wrote the following could are a response to those questions and thought it would be helpful to share. Storage cards show up in the file system as temporary directories. This program examines the objects in the root of the device and any folders that have temp attribute are considered to be a positive match

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace StorageCardInfo
{
    class Program
    {
        const ulong Megabyte = 1048576;
        const ulong Gigabyte = 1073741824;

        [DllImport("CoreDLL")]
        static extern int GetDiskFreeSpaceEx(
        string DirectoryName,
        out ulong lpFreeBytesAvailableToCaller,
        out ulong lpTotalNumberOfBytes,
        out ulong lpTotalNumberOfFreeBytes 
    );

    static void Main(string[] args)
    {
        DirectoryInfo root = new DirectoryInfo("\\");
        DirectoryInfo[] directoryList = root.GetDirectories();
        ulong FreeBytesAvailable;
        ulong TotalCapacity;
        ulong TotalFreeBytes;

        for (int i = 0; i < directoryList.Length; ++i)
        {
            if ((directoryList.Attributes & FileAttributes.Temporary) != 0)
            {
                GetDiskFreeSpaceEx(directoryList.FullName, out FreeBytesAvailable, out TotalCapacity, out TotalFreeBytes);
                Console.Out.WriteLine("Storage card name: {0}", directoryList.FullName);
                Console.Out.WriteLine("Available Bytes : {0}", FreeBytesAvailable);
                Console.Out.WriteLine("Total Capacity : {0}", TotalCapacity);
                Console.Out.WriteLine("Total Free Bytes : {0}", TotalFreeBytes);
            }
        }
    }
}

OTHER TIPS

The mount point is usually "\Storage Card" but can be localized into other languages or modified by OEMs (some devices use "\SD Card" or other mount points, and some devices support mounting multiple storage media). The best way to enumerate the available cards is to use FindFirstFlashCard and FindNextFlashCard.

Both functions fill in a WIN32_FIND_DATA structure. The most important field is cFileName, which will contain the path to the card's mount point (e.g. "\Storage Card").

Note that the device's internal memory will also be enumerated by these functions. If you only care about external volumes, ignore the case where cFileName is an empty string ("").

Using these functions require you to #include <projects.h> and link with note_prj.lib. Both are included in the Windows Mobile SDKs for WM 2000 and later.

I've found using the FindFirstFlashCard/FindNextFlashCard APIs to be more reliable than enumerating directories and checking the temporary flag (which will return bluetooth shared folders for example).

The following sample application demonstrates how to use them and the required P/Invoke statements.

using System;
using System.Runtime.InteropServices;

namespace RemovableStorageTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string removableDirectory = GetRemovableStorageDirectory();
            if (removableDirectory != null)
            {
                Console.WriteLine(removableDirectory);
            }
            else
            {
                Console.WriteLine("No removable drive found");
            }
        }

        public static string GetRemovableStorageDirectory()
        {
            string removableStorageDirectory = null;

            WIN32_FIND_DATA findData = new WIN32_FIND_DATA();
            IntPtr handle = IntPtr.Zero;

            handle = FindFirstFlashCard(ref findData);

            if (handle != INVALID_HANDLE_VALUE)
            {
                do
                {
                    if (!string.IsNullOrEmpty(findData.cFileName))
                    {
                        removableStorageDirectory = findData.cFileName;
                        break;
                    }
                }
                while (FindNextFlashCard(handle, ref findData));
                FindClose(handle);
            }

            return removableStorageDirectory;
        }

        public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);

        // The CharSet must match the CharSet of the corresponding PInvoke signature
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct WIN32_FIND_DATA
        {
            public int dwFileAttributes;
            public FILETIME ftCreationTime;
            public FILETIME ftLastAccessTime;
            public FILETIME ftLastWriteTime;
            public int nFileSizeHigh;
            public int nFileSizeLow;
            public int dwOID;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string cFileName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
            public string cAlternateFileName;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct FILETIME
        {
            public int dwLowDateTime;
            public int dwHighDateTime;
        };

        [DllImport("note_prj", EntryPoint = "FindFirstFlashCard")]
        public extern static IntPtr FindFirstFlashCard(ref WIN32_FIND_DATA findData);

        [DllImport("note_prj", EntryPoint = "FindNextFlashCard")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public extern static bool FindNextFlashCard(IntPtr hFlashCard, ref WIN32_FIND_DATA findData);

        [DllImport("coredll")]
        public static extern bool FindClose(IntPtr hFindFile);
    }
}

There's a pure C# way to do this without native calls.

Taken from here.

//codesnippet:06EE3DE0-D469-44DD-A15F-D8AF629E4E03
public string GetStorageCardFolder()
{
   string storageCardFolder = string.Empty;
   foreach (string directory in Directory.GetDirectories("\\"))
   {
       DirectoryInfo dirInfo = new DirectoryInfo(directory);

       //Storage cards have temporary attributes do a bitwise check.
        //http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=612136&SiteID=1
        if ((dirInfo.Attributes & FileAttributes.Temporary) == FileAttributes.Temporary)
            storageCardFolder = directory;
    }

    return storageCardFolder;
}

Can't add a comment on the TreeUK and ctacke discusion below :

This isn't guaranteed to find a Storage Card - many devices mount built-in flash in teh same way, and it would show up in this list as well. – ctacke May 8 at 18:23
This has worked well for me on HTC and Psion devices. What devices are you aware this doesn't work on? Would be worth seeing if there's another attribute you can discount the build in flash memory with. – TreeUK May 9 at 22:29

To give a idea on a Motorola MC75 (used to be SymboL), i used this piece (of native) code :

    WIN32_FIND_DATA cardinfo;
HANDLE  card = FindFirstFlashCard(&cardinfo);
if (card != INVALID_HANDLE_VALUE)
{
    TCHAR existFile[MAX_PATH];

    wprintf(_T("found : %s\n"), cardinfo.cFileName);

    while(FindNextFlashCard(card, &cardinfo))
    {
        wprintf(_T("found : %s\n"), cardinfo.cFileName);
    }
}
FindClose(card);

Debug output :

cardinfo.dwFileAttributes   0x00000110  unsigned long int
cardinfo.cFileName          "Application"   wchar_t[260]

cardinfo.dwFileAttributes   0x00000110  unsigned long int
cardinfo.cFileName          "Cache Disk"    wchar_t[260]

cardinfo.dwFileAttributes   0x00000110  unsigned long int
cardinfo.cFileName          "Storage Card"  wchar_t[260]

The "Application" and "Cache disk" are internal Flash drives. The "Storage Card" a removable SD Card. All are marked as a FlashDrive (which they are), but only "Storage Card" is removable.

I have combined a number of the solutions above, particularly qwlice's code, to find SD cards on a range of devices. This solution finds SD cards only (so excludes all of the internal "storage cards" that some devices have) without using native dll calls.

The code searches the HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\ key for keys containing "SD" as the name varies slightly on some devices, finds the default mount dir and then looks for temp directories that start with this. This means that it will find \StorageCard2, \StorageCard3, etc.

I have been using this on a range of Intermec and Motorola/Symbol devices and haven't had any problems. Here is the code below:

public class StorageCardFinder
{
    public static List<string> GetMountDirs()
    {
        //get default sd card folder name
        string key = @"HKEY_LOCAL_MACHINE\System\StorageManager\Profiles";
        RegistryKey profiles = Registry.LocalMachine.OpenSubKey(@"System\StorageManager\Profiles");
        string sdprofilename = profiles.GetSubKeyNames().FirstOrDefault(k => k.Contains("SD"));
        if (sdprofilename == null)
            return new List<string>();

        key += "\\" + sdprofilename;
        string storageCardBaseName = Registry.GetValue(key, "Folder", "Storage Card") as String;
        if (storageCardBaseName == null)
            return new List<string>();

        //find storage card
        List<string> cardDirectories = GetFlashCardMountDirs();

        List<string> storageCards = new List<string>();
        foreach (string flashCard in GetFlashCardMountDirs())
        {
            string path = flashCard.Trim();
            if (path.StartsWith(storageCardBaseName))
            {
                storageCards.Add("\\" + path);
            }
        }
        return storageCards;
    }

    private static List<string> GetFlashCardMountDirs()
    {
        DirectoryInfo root = new DirectoryInfo("\\");
        return root.GetDirectories().Where(d => (d.Attributes & FileAttributes.Temporary) != 0)
                                    .Select(d => d.Name).ToList();
    }
}

I post here the code that I use to get the mount dirs of the storage cards. The part where I get the flash cards paths is copied from Sibly's post with a few changes.

The main difference is in that I search through the mount dirs of all the flash cards and I keep the one(s) that match the default storage card name that I read from windows' registry.

It solves the problem that one has on motorola's smart devices where there are multiple flash cards and only one sd card reader whose mount dir's name can change from the default by the numeric suffix (ie. in english WM systems: 'Storage Card' , 'Storage Card2' and so on). I tested it on some motorola models (MC75, MC75A, MC90, MC65) with WM 6.5 english.

This solution should work well with different windows mobile's languages but I don't know if it can deal with those that change the default name of the storage cards. It all depends whether the device's manufacturer updates the windows registry with the new default name.

It would be great if you can test it on different WMs or devices. Feedback is welcome.

   //
   // the storage card is a flash drive mounted as a directory in the root folder 
   // of the smart device
   //
   // on english windows mobile systems the storage card is mounted in the directory "/Storage Card", 
   // if that directory already exists then it's mounted in "/Storage Card2" and so on
   //
   // the regional name of the mount base dir of the storage card can be found in
   // the registry at [HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SDMemory\Folder]
   //  
   // in order to find the path of the storage card we look for the flash drive that starts 
   // with the base name
   //

   public class StorageCard
   {
      private StorageCard()
      {
      }

      public static List<string> GetMountDirs()
      {
         string key = @"HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SDMemory";
         string storageCardBaseName = Registry.GetValue(key, "Folder", "Storage Card") as String;
         List<string> storageCards = new List<string>();
         foreach (string flashCard in GetFlashCardMountDirs())
         {
            string path = flashCard.Trim();
            if (path.StartsWith(storageCardBaseName))
            {
               storageCards.Add(path);
            }
         }
         return storageCards;
      }

      private static List<string> GetFlashCardMountDirs()
      {
         List<string> storages = new List<string>();

         WIN32_FIND_DATA findData = new WIN32_FIND_DATA();
         IntPtr handle = IntPtr.Zero;

         handle = FindFirstFlashCard(ref findData);

         if (handle != INVALID_HANDLE_VALUE)
         {
            do
            {
               if (!string.IsNullOrEmpty(findData.cFileName))
               {
                  storages.Add(findData.cFileName);
                  storages.Add(findData.cAlternateFileName);
               }
            }
            while (FindNextFlashCard(handle, ref findData));
            FindClose(handle);
         }

         return storages;
      }

      private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);    

      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
      private struct WIN32_FIND_DATA
      {
         public int dwFileAttributes;
         public FILETIME ftCreationTime;
         public FILETIME ftLastAccessTime;
         public FILETIME ftLastWriteTime;
         public int nFileSizeHigh;
         public int nFileSizeLow;
         public int dwOID;
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
         public string cFileName;
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
         public string cAlternateFileName;
      }

      [StructLayout(LayoutKind.Sequential)]
      private struct FILETIME
      {
         public int dwLowDateTime;
         public int dwHighDateTime;
      };

      [DllImport("note_prj", EntryPoint = "FindFirstFlashCard")]
      private extern static IntPtr FindFirstFlashCard(ref WIN32_FIND_DATA findData);

      [DllImport("note_prj", EntryPoint = "FindNextFlashCard")]
      [return: MarshalAs(UnmanagedType.Bool)]
      private extern static bool FindNextFlashCard(IntPtr hFlashCard, ref WIN32_FIND_DATA findData);

      [DllImport("coredll")]
      private static extern bool FindClose(IntPtr hFindFile);
   }

On Windows CE 5 (which is the base for Windows Mobile 6) the storage cards get mounted at the root file system as "Storage Card\", "Storage Card2\", etc.

To find out if it's mounted call GetFileAttributes (or the remote version CeGetFileAttributes I believe) passing in the full path ("\Storage Card\"). If it returns INVALID_FILE_ATTRIBUTES then it's not mounted, otherwise check to make sure it's a directory before returning true.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top