Get drive label in C#

Rick picture Rick · May 16, 2010 · Viewed 10.7k times · Source

When I use System.IO.DriveInfo.GetDrives() and look at the .VolumeLabel property of one of the drives, I see "PATRIOT XT", which is indeed the drive's volume label.

If I open "My Computer", instead I see "TrueCrypt Traveler Disk", and I can't seem to find any way to programmatically retrieve that value as none of the DriveInfo properties hold that value. I also tried querying the information via WMI's Win32_LogicalDisk, but no properties contained that value there either.

So any idea what the label My Computer uses is called, and more importantly, how to programmatically retrieve it?

EDIT: To be clear, here's the code I'm using, followed by what it outputs, followed by what I see in My Computer (which is what I want to duplicate):

foreach (DriveInfo DI in DriveInfo.GetDrives())
    richTextBox1.AppendText(
        (
            DI.IsReady ?
            (DI.VolumeLabel.Length == 0 ? DI.DriveType.ToString() : DI.VolumeLabel) :
            DI.DriveType.ToString()
        )
        +
        " (" + DI.Name.Replace("\\", "") + ")"
        + Environment.NewLine
    );
Removable (A:)
Fixed (C:)
CDRom (D:)
PATRIOT XT (E:)
Backup (Y:)
Data (Z:)

My Computer details view displays:

Floppy Disk Drive (A:)
Local Disk (C:)
DVD RW Drive (D:)
TrueCrypt Traveler Disk (E:)
Backup (Y:)
Data (Z:)

Answer

Mike Makarov picture Mike Makarov · Mar 22, 2015

Unfortunately, to get this information without hacks and weird tricks, you need to use the P/Invoke technique. There are 2 options:

  1. Get the real label set by user or system. This could be "New volume", "Install (\Server)", "Contoso Pro Installation disk 4" and so on.
  2. Get the label exactly as it is shown in Explorer (My computer / This PC window). This is the same as (1) but it follows user preferences set in Folder Options dialog, e.g. "Hide drive letter". Example: "New volume (Q:)"

To get the information as explained in option (1), you'll have to use the following code:

    public const string SHELL = "shell32.dll";

    [DllImport(SHELL, CharSet = CharSet.Unicode)]
    public static extern uint SHParseDisplayName(string pszName, IntPtr zero, [Out] out IntPtr ppidl, uint sfgaoIn, [Out] out uint psfgaoOut);
    
    [DllImport(SHELL, CharSet = CharSet.Unicode)]
    public static extern uint SHGetNameFromIDList(IntPtr pidl, SIGDN sigdnName, [Out] out String ppszName);
    
    public enum SIGDN : uint
    {
        NORMALDISPLAY = 0x00000000,
        PARENTRELATIVEPARSING = 0x80018001,
        DESKTOPABSOLUTEPARSING = 0x80028000,
        PARENTRELATIVEEDITING = 0x80031001,
        DESKTOPABSOLUTEEDITING = 0x8004c000,
        FILESYSPATH = 0x80058000,
        URL = 0x80068000,
        PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
        PARENTRELATIVE = 0x80080001
    }
    
    //var x = GetDriveLabel(@"C:\")
    public string GetDriveLabel(string driveNameAsLetterColonBackslash)
    {
        IntPtr pidl;
        uint dummy;
        string name;
        if (SHParseDisplayName(driveNameAsLetterColonBackslash, IntPtr.Zero, out pidl, 0, out dummy) == 0
            && SHGetNameFromIDList(pidl, SIGDN.PARENTRELATIVEEDITING, out name) == 0
            && name != null)
        {
            return name;
        }
        return null;
    }

For option (2), replace SIGDN.PARENTRELATIVEEDITING with SIGDN.PARENTRELATIVE or SIGDN.NORMALDISPLAY.

Note: for option 2, there's also 1-call method using ShGetFileInfo(), but it calls these methods anyway, and is less flexible, so I do not post it here.

Note 2: keep in mind, the signature of SHGetNameFromIDList() is optimized in this example. In case the drive label is used only temporary (especially if it is re-read from time to time) this example introduces small memory leak. To avoid it, declare last parameter as out IntPtr, and then use something like

     var tmp = Marshal.PtrToStringUni(ppszName);
     Marshal.FreeCoTaskMem(ppszName);

Note 3: this works over Windows shell, so it will return what user expects, regardless of the source of this label - volume label, user edit, Autorun.inf file or anything else.