1

I want to clean usb using diskpart from C#. I have written below code in C# to get all connect USB. And I iterate thru all usb and clean each usb using diskpart below cmd command.

  diskpart
   list disk
   select disk <0/1/2>
   clean

I want to get disk number <0/1/2> from drive name so that I can clean each usb one after another.

foreach (DriveInfo drive in DriveInfo.GetDrives())
                {
                    if (drive.IsReady == true)
                    {
                        if (drive.DriveType == DriveType.Removable)
                        {
                          string usbName = drive.Name;
                        }
                     }
}

USB without partition

Milan
  • 47
  • 6
  • Foozinator's answer [here](https://stackoverflow.com/questions/412632/how-do-i-retrieve-disk-information-in-c) helps to list the drives in their drive letter order. A few extra checks are probably called for. (On my machine an offline drive is included, which is ok, but at the end 3 more are listed, which I really can't explain) And a direct numer would indeed be nice to get.. – TaW Oct 31 '22 at 10:57

2 Answers2

1

The following shows how to use ManagementObjectSearcher, ManagementObject to retrieve a list of removable USB drives

Create a Windows Forms App (.NET Framework) project

Add Reference (System.Management)

VS 2022:

  • Click Project
  • Select Add Reference...
  • Click Assemblies
  • Check System.Management
  • Click OK

Add using directives

  • using System.IO;
  • using System.Management;
  • using System.Diagnostics;

Create a class (name: LogicalDiskInfo)

public class LogicalDiskInfo : IComparable<LogicalDiskInfo>
{
    public string Description { get; set; }
    public string DeviceID { get; set; }
    public uint DiskIndex { get; set; }
    public uint DriveType { get; set; }
    public string FileSystem { get; set; }
    public bool IsRemovable { get; set; } = false;
    public string Name { get; set; }
    public uint PartitionIndex { get; set; }
    public uint PartitionNumber { get; set; }
    public UInt64 Size { get; set; }

    public int CompareTo(LogicalDiskInfo other)
    {
        if (String.Compare(this.Name, other.Name) == 0)
            return 0;
        else if (String.Compare(this.Name, other.Name) < 0)
            return -1;
        else
            return 1;
    }
}

Create a class (name: LogicalDisk)

public class LogicalDisk
{
    public List<LogicalDiskInfo> LogicalDiskInfos = new List<LogicalDiskInfo>();
}

Create a class (name: DiskDriveInfo)

public class DiskDriveInfo : IComparable<DiskDriveInfo>
{
    public string Caption { get; set; } = string.Empty;
    public string DeviceID { get; set; } = string.Empty;
    public List<LogicalDiskInfo> LogicalDisks { get; set; } = new List<LogicalDiskInfo>();
    public UInt32 DiskIndex { get; set; } = 0;
    public string InterfaceType { get; set; } = string.Empty;
    public bool IsRemovable { get; set; } = false;
    public string MediaType { get; set; }
    public string Model { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public UInt32 Partitions { get; set; } = 0;
    public string PnpDeviceID { get; set; } = string.Empty;
    public UInt64 Size { get; set; } = 0;
    public string Status { get; set; } = string.Empty;

    public int CompareTo(DiskDriveInfo other)
    {
        if (this.DiskIndex == other.DiskIndex)
            return 0;
        else if (this.DiskIndex < other.DiskIndex)
            return -1;
        else
            return 1;
    }
}

GetUSBRemovableDiskDriveInfo:

Note: In Windows 10, it's possible to create multiple partitions on a USB flash drive. See here for more info. Therefore, it's possible that more than one drive letter may exist on the same physical disk drive. The code below works with USB drives having either a single partition or multiple partitions.

private List<DiskDriveInfo> GetUSBRemovableDiskDriveInfo()
{
    SortedDictionary<uint, DiskDriveInfo> diskDict = new SortedDictionary<uint, DiskDriveInfo>();

    List<DiskDriveInfo> driveInfos = new List<DiskDriveInfo>();

    //MediaType: 'Removable Media'
    using (ManagementObjectSearcher searcherDiskDrive = new ManagementObjectSearcher("SELECT Caption, DeviceID, Index, InterfaceType, MediaType, Model, Name, Partitions, PNPDeviceID, Size, Status FROM Win32_DiskDrive WHERE InterfaceType='USB' and MediaType='Removable Media'"))
    {
        foreach (ManagementObject objDiskDrive in searcherDiskDrive.Get())
        {
            if (objDiskDrive == null)
                continue;

            //create new instance
            DiskDriveInfo ddInfo = new DiskDriveInfo();

            //set value
            uint diskIndex = Convert.ToUInt32(objDiskDrive["Index"]);

            ddInfo.Caption = objDiskDrive["Caption"]?.ToString();
            ddInfo.DeviceID = objDiskDrive["DeviceID"]?.ToString();
            ddInfo.DiskIndex = diskIndex;
            ddInfo.InterfaceType = objDiskDrive["InterfaceType"]?.ToString();
            ddInfo.MediaType = objDiskDrive["MediaType"]?.ToString();
            ddInfo.Model = objDiskDrive["Model"]?.ToString();
            ddInfo.Name = objDiskDrive["Name"]?.ToString();
            ddInfo.Partitions = Convert.ToUInt32(objDiskDrive["Partitions"]);
            ddInfo.PnpDeviceID = objDiskDrive["PnpDeviceID"]?.ToString();
            ddInfo.Size = Convert.ToUInt64(objDiskDrive["Size"]);
            ddInfo.Status = objDiskDrive["Status"]?.ToString();

            if (ddInfo.MediaType == "Removable Media")
                ddInfo.IsRemovable = true;
            else
                ddInfo.IsRemovable = false;


            if (!diskDict.ContainsKey(diskIndex))
            {
                //Debug.WriteLine($"Adding DiskIndex {ddInfo.DiskIndex} Partitions: {ddInfo.Partitions}");

                //add
                diskDict.Add(diskIndex, ddInfo);
            }
        }
    }

    //create new instance
    SortedDictionary<string, LogicalDisk> logicalDiskToPartitionDict = new SortedDictionary<string, LogicalDisk>();

    //get info from Win32_LogicalDiskToPartition
    //this is used to associate a DiskIndex and PartitionIndex with a drive letter
    using (ManagementObjectSearcher searcherLogicalDiskToPartition = new ManagementObjectSearcher($@"SELECT * FROM Win32_LogicalDiskToPartition"))
    {
        foreach (ManagementObject objLogicalDiskToPartition in searcherLogicalDiskToPartition.Get())
        {
            if (objLogicalDiskToPartition == null)
                continue;

            string antecedent = objLogicalDiskToPartition["Antecedent"]?.ToString();
            string dependent = objLogicalDiskToPartition["Dependent"]?.ToString();
            
            string antecedentValue = antecedent.Substring(antecedent.IndexOf('=') + 1).Replace("\"", "");

            uint diskIndex = 0;
            uint partitionIndex = 0;

            //get disk index and convert to uint
            UInt32.TryParse(antecedentValue.Substring(antecedentValue.IndexOf("#") + 1, antecedentValue.IndexOf(",") - (antecedentValue.IndexOf("#") + 1)), out diskIndex);

            //get partition index and convert to uint
            UInt32.TryParse(antecedentValue.Substring(antecedentValue.LastIndexOf("#") + 1), out partitionIndex);

            string driveLetter = dependent.Substring(dependent.IndexOf("=") + 1).Replace("\"", "");

            if (diskDict.ContainsKey(diskIndex))
            {
                if (!logicalDiskToPartitionDict.ContainsKey(driveLetter))
                {
                    //add
                    logicalDiskToPartitionDict.Add(driveLetter, new LogicalDisk());
                }

                //get info from Win32_LogicalDisk
                using (ManagementObjectSearcher searcherLogicalDisk = new ManagementObjectSearcher($"SELECT Description, DeviceID, DriveType, FileSystem, Name, Size FROM Win32_LogicalDisk WHERE Name = '{driveLetter}'"))
                {
                    foreach (ManagementObject objLogicalDisk in searcherLogicalDisk.Get())
                    {
                        if (objLogicalDisk == null)
                            continue;

                        //create new instance
                        LogicalDiskInfo logicalDiskInfo = new LogicalDiskInfo();

                        //set value
                        logicalDiskInfo.Description = objLogicalDisk["Description"]?.ToString();
                        logicalDiskInfo.DeviceID = objLogicalDisk["DeviceID"]?.ToString();
                        logicalDiskInfo.DriveType = Convert.ToUInt32(objLogicalDisk["DriveType"]);
                        logicalDiskInfo.DiskIndex = diskIndex;
                        logicalDiskInfo.FileSystem = objLogicalDisk["FileSystem"]?.ToString();
                        logicalDiskInfo.Name = objLogicalDisk["Name"]?.ToString();
                        logicalDiskInfo.PartitionIndex = partitionIndex;
                        logicalDiskInfo.PartitionNumber = partitionIndex + 1; //diskpart partitions start at 1
                        logicalDiskInfo.Size = Convert.ToUInt64(objLogicalDisk["Size"]);

                        //DriveType: 2=Removable; 3=Local Disk; 4=Network Drive; 5=CD
                        if (logicalDiskInfo.DriveType == 2)
                            logicalDiskInfo.IsRemovable = true;
                        else
                            logicalDiskInfo.IsRemovable = false;

                        Debug.WriteLine($"adding logicalDiskInfo for DiskIndex: '{diskIndex}' PartitionIndex: '{partitionIndex}' PartitionNumber: '{logicalDiskInfo.PartitionNumber}'");

                        //add 
                        logicalDiskToPartitionDict[driveLetter].LogicalDiskInfos.Add(logicalDiskInfo);
                    }
                }
            }
            
        }
    }

    //add logical disk info to disk dictionary
    foreach(KeyValuePair<string, LogicalDisk> kvp in logicalDiskToPartitionDict)
    {
        List<LogicalDiskInfo> logicalDiskInfoList = kvp.Value.LogicalDiskInfos;
        
        //sort
        logicalDiskInfoList.Sort();

        foreach (LogicalDiskInfo ldInfo in logicalDiskInfoList)
        {
            //add
            diskDict[ldInfo.DiskIndex].LogicalDisks.Add(ldInfo);
        }
    }

    //only add disks that are listed as 'Removable'
    foreach(KeyValuePair<uint, DiskDriveInfo> kvp in diskDict)
    {
        if (kvp.Value.IsRemovable)
        {
            //add
            driveInfos.Add(kvp.Value);
        }
    }

    return driveInfos;
}

Usage:

System.Diagnostics.Debug.WriteLine("--------GetUSBRemovableDiskDriveInfo----------");

foreach (DiskDriveInfo ddInfo in GetUSBRemovableDiskDriveInfo())
{
    string driveLetters = string.Empty;

    for (int i = 0; i < ddInfo.LogicalDisks.Count; i++)
    {
        if (i > 0)
            driveLetters += ", ";

        driveLetters += ddInfo.LogicalDisks[i].Name;
    }

    System.Diagnostics.Debug.WriteLine($"Caption: '{ddInfo.Caption}' Name: '{ddInfo.Name}' DiskIndex: '{ddInfo.DiskIndex}' DriveLetters: [{driveLetters}] Partitions: '{ddInfo.Partitions}' Size: '{ddInfo.Size}'");
}

One can use System.Diagnostics.Process to execute a diskpart script to clean one or more disks. See this post for more info.

Resources:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • Thanks for the solution. But this sample code works well when we have USB with Partition. But I have certain USB which don't have any partition and volume in it. – Milan Nov 01 '22 at 05:40
  • I have added Image in question. Where I have USB which don't have any partition and volume. Even I want this kind of USB so that I can create Partition from diskpart script. – Milan Nov 01 '22 at 05:45
  • I found Win32_LogicalDiskToPartition only works when we have USB which have partition. And I don't find any direct relationship between Win32_DiskDrive & Win32_LogicalDisk to get USB letter and index both. – Milan Nov 01 '22 at 05:50
  • The OP only mentions using `diskpart` to "clean" a disk which removes all partitions. There wouldn't be a need to "clean" a disk without any partitions. A disk without any partitions doesn't have a drive letter associated with it. – Tu deschizi eu inchid Nov 01 '22 at 13:38
  • The code has been modified to include USB removable drives without any partitions. – Tu deschizi eu inchid Nov 01 '22 at 15:36
  • 1
    @user9938: "A disk without any partitions doesn't have a drive letter associated with it" is incorrect. If the disk has an empty partition table, then yes there would be no drive letter. But if the disk is seen as a "superfloppy", then the entire block device is formatted with a filesystem without subdivision, and this filesystem definitely gets assigned a drive letter (by default; Disk Management can remove the drive letter and optionally mount the filesystem under an empty NTFS directory) – Ben Voigt Nov 01 '22 at 18:42
  • @BenVoigt: I'm unfamiliar with a "superfloppy". In Windows 10, how would one create a "superfloppy" using `cmd`, `diskpart` or `Disk Management` (diskmgmt.msc)? – Tu deschizi eu inchid Nov 01 '22 at 20:52
  • @user9938: One doesn't. Whether a disk is treated as a superfloppy or needs a partition table depends on feature flags returned from the disk driver. For the few models of removable storage which can operate in either mode, it's because the driver reads some secret flag bits from the flash drive, which requires a manufacturer-specific tool to change. https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-and-gpt-faq?view=windows-11#superfloppy http://reboot.pro/index.php?showtopic=4560&page=1 – Ben Voigt Nov 01 '22 at 21:02
  • Man if I had a nickel for every time mounting a drive as an folder in another volume saved my tail... I would have a lot of nickles! :-) – Sabre Nov 02 '22 at 16:32
1

It may be the dirty way, but you could just use diskpart interactively and send commands / parse output. Since we are invoking diskpart anyway, a 100% managed codebase seems to not be the target as much as a way of getting it done.

So emphasis on "Dirty" way, but very simple functional way as well :-)

detail disk

Sometimes when there is a perfectly good tool for the job such as ghostscript or ffmpeg, I automate them this way. Just as if I were typing at the CMD prompt.

Wrap a CMD instance in a wrapper to read/write to, and power it in event driven logic from there.

using System;
using System.Diagnostics;


namespace yourapp
{
    public class cmdShell
    {
        private Process shellProcess;

        public delegate void onDataHandler(cmdShell sender, string e);
        public event onDataHandler onData;

        public cmdShell()
        {
            try
            {
                shellProcess = new Process();
                ProcessStartInfo si = new ProcessStartInfo("cmd.exe");
                si.Arguments = "/k";
                si.RedirectStandardInput = true;
                si.RedirectStandardOutput = true;
                si.RedirectStandardError = true;
                si.UseShellExecute = false;
                si.CreateNoWindow = true;
                si.WorkingDirectory = Environment.GetEnvironmentVariable("windir");
                shellProcess.StartInfo = si;
                shellProcess.OutputDataReceived += shellProcess_OutputDataReceived;
                shellProcess.ErrorDataReceived += shellProcess_ErrorDataReceived;
                shellProcess.Start();
                shellProcess.BeginErrorReadLine();
                shellProcess.BeginOutputReadLine();
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
        }

        void shellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            doOnData(e.Data);
        }

        void shellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            doOnData(e.Data);
        }

        private void doOnData(string data)
        {
            if (onData != null) onData(this, data);
        }

        public void write(string data)
        {
            try
            {
                shellProcess.StandardInput.WriteLine(data);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
        }
    }
}
Sabre
  • 2,350
  • 2
  • 18
  • 25
  • This solution is really great. But I have a requirement where I have to avoid user interaction. And to implement desktop application which shows all the USB which are connected with system and don't have any partition. And application is responsible to create partition without any human interaction. – Milan Nov 02 '22 at 07:11
  • And When I am querying this Win32_LogicalDisk class it gives me the letter of USB even though it doesn't have any partition. But I am not getting index of disk (USB). – Milan Nov 02 '22 at 07:16
  • This can do it without user interaction. Have your code emulate what the user would have done just as they had typed it, then have your code parse the output to decide what to do next. EG: fire up the shell, and send disk[part, you are now in the diskpart utility. send "list disk" get a lit of all disks present. Parse the output, trim each line, select out lines (I suggest regex) that start with "Disk, [space] and a number" not a #. From there you have the count of disks. React by sending for each, "select dick X [CRLF] detail disk" And parse output for line "Type" if == USB, send "clean". – Sabre Nov 02 '22 at 13:51
  • NO human interaction there, just code deciding what the user would have. BTW, works for anything you could have done at the CMD prompt as well, for instance "WMIC DiskDrive list /format:list" which would also give you the list of disks' id and and their type. Index and InterfaceType respectively. Parse the strings returned and build your logic from that. (BTW detecting partitions woudl be list volume on diskpart after selecting disk, none = no partition, or property "Partitions" on the above WMI query. :-) – Sabre Nov 02 '22 at 13:59
  • I cannot edit the comment now, but since I suggested that, it occurred to me, the /format option has CSV as well, which may make your scenario far easier if you choose WMI "WMIC DiskDrive list /format:csv" – Sabre Nov 02 '22 at 16:22