Get drive label in C#

前端 未结 8 1995
臣服心动
臣服心动 2020-12-20 12:20

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 dr

相关标签:
8条回答
  • 2020-12-20 12:35

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

    0 讨论(0)
  • 2020-12-20 12:38

    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 讨论(0)
  • 2020-12-20 12:39

    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
        }
    
    0 讨论(0)
  • 2020-12-20 12:52

    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;
        }
    
    0 讨论(0)
  • 2020-12-20 12:52

    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.

    0 讨论(0)
  • 2020-12-20 12:53

    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.

    0 讨论(0)
提交回复
热议问题