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?
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.