Search code examples
c#.netvisual-studio-2019wmisd-card

How to get the drive letter of an SD Card connected to a PC, using C#


How to get the drive letter of an SD Card connected to a PC, from a C# .NET Framework application?

I have looked at suggested questions on this topic, including this, this & this, but none of them give me the solution I need.

Using System.IO.DriveInfo.GetDrives() or System.Management.ManagementObjectSearcher() with query "Win32_LogicalDisk", I can get the drive letters of all devices, but I can't tell which device(s) is the SD card. Using System.Management.ManagementObjectSearcher() with query "CIM_LogicalDevice", "Caption = 'SDHC Card'", I get 2 devices with the "SDHC Card" caption property, but no drive letters.

How can I get the drive letter of the SD Card or card reader?

Here is what I have tried so far:

using System;
using System.Management;

namespace Code3
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("\tfrom: 'ManagementObjectSearcher()' with query \"Win32_LogicalDisk\"");
            var searcher1 = new ManagementObjectSearcher(@"\root\cimv2", "SELECT * FROM Win32_LogicalDisk");
            foreach (ManagementBaseObject disk in searcher1.Get())
            {
                string diskID = disk.GetPropertyValue("DeviceID").ToString();
                int driveType = Convert.ToInt32(disk.GetPropertyValue("DriveType"));
                string diskCaption = disk.GetPropertyValue("Caption").ToString();
                string diskDescription = disk.GetPropertyValue("Description").ToString();
                string diskName = disk.GetPropertyValue("Name").ToString();
                int diskMediaType = Convert.ToInt32(disk.GetPropertyValue("MediaType"));
                Console.WriteLine($"{diskName} - ID: {diskID},  Caption: {diskCaption},  Desc.: {diskDescription,-16},  Drive Type: {driveType},  Media Type: {diskMediaType}.");
            }
            Console.WriteLine();

            Console.WriteLine("\tfrom: 'ManagementObjectSearcher()' with query SelectQuery(\"CIM_LogicalDevice\", \"Caption = 'SDHC Card'\")");
            ManagementScope mgmtScope = new ManagementScope(@"\root\cimv2");
            SelectQuery devQuery = new SelectQuery("CIM_LogicalDevice", "Caption = 'SDHC Card'");
            var searcher2 = new ManagementObjectSearcher(mgmtScope, devQuery);

            foreach (ManagementBaseObject device in searcher2.Get())
            {
                Console.WriteLine($"{device.GetPropertyValue("Name"),-15} - Caption: {device.GetPropertyValue("Caption")},  Device ID: {device.GetPropertyValue("DeviceID")}.");
                continue; // ... to skip property display

                if (!string.IsNullOrEmpty(device.GetPropertyValue("Name").ToString()))
                {
                    PropertyDataCollection props = device.Properties;
                    Console.WriteLine($"\n\t\tProperties of {device.GetPropertyValue("DeviceID")} Drive: \n");
                    foreach (var prop in device.Properties)
                    {
                        if (prop.Value != null)
                            Console.WriteLine($"{prop.Name,-20} - {prop.Type,-8} - {prop.Value ?? "(null)"}");
                    }
                    Console.WriteLine();
                }
            }

            Console.ReadKey();
        }
    }
}

Thank you for any help you can give me.

EDIT: From "CIM_LogicalDisk", I can see that "F:" drive is my SD-Card. (from 'VolumeName' property.)

From "CIM_LogicalDevice", I can see the "\.\PHYSICALDRIVE1" and "PCISTOR\DISK&VEN_RSPER&PROD_RTS5208LUN0&REV_1.00\0000" is my SD-Card. (from 'Name', 'Caption', and/or 'Model' properties.)

But my app can't see this! Note that 'drive letter' and 'PHYSICALDRIVE number' do not remain correlated, and can change as different removable devices are inserted and removed.

How can I get my code to make the connection between logical and physical drives?


Solution

  • I finally got a solution worked out. Using WMI association classes, I was able to make the connection between logical and physical drives.

    This class is my solution:

    using System.Collections.Generic;
    using System.Management;
    
    namespace GetSDCard
    {
        public class GetSDCards
        {
            public Card[] GetCards()
            {
                return FindCards().ToArray();
            }
    
            private List<Card> FindCards()
            {
                List<Card> cards = new List<Card>();
    
                //  Get Disk Drives collection (Win32_DiskDrive)
                string queryDD = "SELECT * FROM Win32_DiskDrive WHERE Caption = 'SDHC Card'";
                using (ManagementObjectSearcher searchDD = new ManagementObjectSearcher(queryDD))
                {
                    ManagementObjectCollection colDiskDrives = searchDD.Get();
                    foreach (ManagementBaseObject objDrive in colDiskDrives)
                    {
                        //  Get associated Partitions collection (Win32_DiskDriveToDiskPartition)
                        string queryPart = $"ASSOCIATORS OF {{Win32_DiskDrive.DeviceID='{objDrive["DeviceID"]}'}} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
                        using (ManagementObjectSearcher searchPart = new ManagementObjectSearcher(queryPart))
                        {
                            ManagementObjectCollection colPartitions = searchPart.Get();
                            foreach (ManagementBaseObject objPartition in colPartitions)
                            {
                                //  Get associated Logical Disk collection (Win32_LogicalDiskToPartition)
                                string queryLD = $"ASSOCIATORS OF {{Win32_DiskPartition.DeviceID='{objPartition["DeviceID"]}'}} WHERE AssocClass = Win32_LogicalDiskToPartition";
                                using (ManagementObjectSearcher searchLD = new ManagementObjectSearcher(queryLD))
                                {
                                    ManagementObjectCollection colLogicalDisks = searchLD.Get();
                                    foreach (ManagementBaseObject objLogicalDisk in colLogicalDisks)
                                        cards.Add(new Card($"{objLogicalDisk["DeviceID"]}", $"{objDrive["Caption"]}", $"{objLogicalDisk["VolumeName"]}"));
                                }
                            }
                        }
                    }
                }
    
                return cards;
            }
    
    
            public class Card
            {
                public string Drive { get; set; }
                public string Name { get; set; }
                public string Label { get; set; }
    
                public Card(string _drive, string _name, string _label)
                {
                    Drive = _drive;
                    Name = _name;
                    Label = _label;
                }
            }
        }
    }
    

    Here is a simple console app to demonstrate how to use it.

    using GetSDCard;
    using System;
    using System.IO;
    
    namespace FindSDCard_Demo
    {
        class Program
        {
            static void Main(string[] args)
            {
                GetSDCards getter = new GetSDCards();
                GetSDCards.Card[] sdCards = getter.GetCards();
                if (sdCards.Length == 0)
                    Console.WriteLine("No SD Cards found.");
                else
                {
                    string sdDrive = sdCards[0].Drive;
                    Console.WriteLine($"Root folder of SD Card '{sdDrive}':");
                    foreach (var folder in Directory.GetDirectories(sdDrive))
                        Console.WriteLine($"\t{folder}");
                }
            }
        }
    }
    

    I hope that this can save you the hours of frustration I went through.