Get drive label in C#

前端 未结 8 2024
臣服心动
臣服心动 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.

提交回复
热议问题