开发者

Get drive label in C#

When I use System.IO.DriveInfo.GetDrives() and look at the .VolumeLabel property 开发者_JS百科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:)


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.


Thanks for tip about autorun.inf. Here is the C# fragment which I created to retrieve the label.

private string GetDriveLabelFromAutorunInf(string drivename)
    {
        try
        {
            string filepathAutorunInf = Path.Combine(drivename, "autorun.Inf");
            string stringInputLine = "";
            if (File.Exists(filepathAutorunInf))
            {
                StreamReader streamReader = new StreamReader(filepathAutorunInf);
                while ((stringInputLine = streamReader.ReadLine()) != null) 
                  {
                      if (stringInputLine.StartsWith("label="))
                          return stringInputLine.Substring(startIndex:6);
                  }
                return "";
            }
            else return "";
        }
        #region generic catch exception, display message box, and terminate
        catch (Exception exception)
        {
            System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(exception, true);
            MessageBox.Show(string.Format("{0} Exception:\n{1}\n{2}\n\n{3}\n\nMethod={4}   Line={5}   Column={6}",
                    trace.GetFrame(0).GetMethod().Module,
                    exception.Message,
                    exception.StackTrace,
                    exception.ToString(),
                    trace.GetFrame(0).GetMethod().Name,
                    trace.GetFrame(0).GetFileLineNumber(),
                    trace.GetFrame(0).GetFileColumnNumber()),
                "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            Environment.Exit(1);
            return "";      // to keep compiler happy
        }
        #endregion
    }


Looks like My Computer looks at the autorun.inf and uses the label= value from the [autorun] section.

Still not quite sure where the "DVD RW Drive" and "Floppy Disk Drive" labels come from, but I guess they may be hardcoded based on the drive type.


I hope the following will help you:

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    static extern bool GetVolumeInformation(string Volume,
        StringBuilder VolumeName, uint VolumeNameSize,
        out uint SerialNumber, out uint SerialNumberLength, out uint flags,
        StringBuilder fs, uint fs_size);

    private void Form1_Load(object sender, EventArgs e)
    {
        uint serialNum, serialNumLength, flags;
        StringBuilder volumename = new StringBuilder(256);
        StringBuilder fstype = new StringBuilder(256);
        bool ok = false;
        Cursor.Current = Cursors.WaitCursor;
        foreach (string drives in Environment.GetLogicalDrives())
        {
            ok = GetVolumeInformation(drives, volumename, (uint)volumename.Capacity - 1, out serialNum,
                                   out serialNumLength, out flags, fstype, (uint)fstype.Capacity - 1);
            if (ok)
            {
                lblVolume.Text = lblVolume.Text + "\n Volume Information of " + drives + "\n";
                lblVolume.Text = lblVolume.Text + "\nSerialNumber of is..... " + serialNum.ToString() + " \n";
                if (volumename != null)
                {
                    lblVolume.Text = lblVolume.Text + "VolumeName is..... " + volumename.ToString() + " \n";
                }
                if (fstype != null)
                {
                    lblVolume.Text = lblVolume.Text + "FileType is..... " + fstype.ToString() + " \n";
                }
            }
            ok = false;
        }
        Cursor.Current = Cursors.Default;
    }


I haven't tried this myself, but in the registry, look for

HKLM/Software/Microsoft/Windows/CurrentVersion/Explorer/DriveIcons/[Drive-Letter]/

then read the

DefaultLabel

key. Also WARNING! writing invalid keys/values to the registry can severely damage your system! Be sure you're certain of what you're doing before proceeding. Here is a resource to help you with accessing the registry programmatically.


This looks like it could be a solution.


It's located in the autorun.inf folder. My Volume Label for my flash drive is simply 16G, but by putting an autorun.inf file with the following text [autorun] label=My 16 gigabyte Flash Drive

and then using attrib to +s +h +r the file, it doesn't show up unless I have show hidden files AND show system files under folder options/view enabled.

To programmatically located this via C#, I honestly haven't tried to open the autorun.inf, but it should be straight forward, check if File.Exists(Drive:\autorun.inf) ignoring the fact that it's +s+h+r (just in case someone has set it), then open it readonly and parse the label= line. If in fact, the file is present, use the autorun label instead of the Volume Label.

I can still change use the autorun.inf label= tag even in Windows 7 to modify label.


Recently I had the same problem that I have been able to solve. Here is how to obtain the labels as they appear in the Windows Explorer:

  1. Add C:\Windows\System32\shell32.dll as a reference.
  2. Add using Shell32;
  3. Instantiate the shell object: dynamic shellObject = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
  4. Get the disk data: var driveData = (Folder2)ShellObject.NameSpace(driveInfo.Name);
  5. The parameter driveData.Name will contain the label (For example: "Local Disk (C:)").

And below is the complete code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Shell32;

namespace VolumeLabels
{
    static class Drives
    {
        [DebuggerDisplay("Name: '{Name,nq}', Path: '{Path,nq}'")]
        public struct DriveNameInfo
        {
            public string Name { get; }
            public string Path { get; }

            public DriveNameInfo(string name, string path)
            {
                Name = name;
                Path = path;
            }

            public override string ToString()
            {
                return Name;
            }
        }

        private static dynamic _shellObject;
        private static dynamic ShellObject => _shellObject ?? (_shellObject = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application")));

        public static IEnumerable<DriveNameInfo> Get()
        {
            foreach (var driveInfo in DriveInfo.GetDrives())
            {
                var driveData = (Folder2)ShellObject.NameSpace(driveInfo.Name);
                if (driveData == null)
                    yield break;
                var driveDataSelf = driveData.Self;

                yield return new DriveNameInfo(driveDataSelf.Name, driveDataSelf.Path);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach (var driveNameInfo in Drives.Get())
                Console.WriteLine(driveNameInfo);

            Console.ReadKey(true);
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜