【问题标题】:Get drive label in C#在 C# 中获取驱动器标签
【发布时间】:2011-02-20 02:21:16
【问题描述】:

当我使用System.IO.DriveInfo.GetDrives() 并查看其中一个驱动器的.VolumeLabel 属性时,我看到“PATRIOT XT”,这确实是驱动器的卷标。

如果我打开“我的电脑”,而是看到“TrueCrypt Traveler Disk”,我似乎找不到任何方法来以编程方式检索该值,因为没有一个DriveInfo 属性持有该值。我也尝试通过 WMI 的Win32_LogicalDisk 查询信息,但那里也没有包含该值的属性。

那么知道我的电脑使用的标签是什么,更重要的是,如何以编程方式检索它?

编辑:为了清楚起见,这是我正在使用的代码,然后是它的输出,然后是我在“我的电脑”中看到的(这是我想要复制的):

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
    );
可拆卸 (A:) 固定 (C:) 光盘 (D:) 爱国者 XT (E:) 备份 (Y:) 数据 (Z:)

我的电脑详情视图显示:

软盘驱动器 (A:) 本地磁盘 (C:) DVD RW 驱动器 (D:) TrueCrypt 旅行者磁盘 (E:) 备份 (Y:) 数据 (Z:)

【问题讨论】:

    标签: c# drive


    【解决方案1】:

    不幸的是,要在不使用 hack 和奇怪技巧的情况下获取此信息,您需要使用 P/Invoke 技术。 有两种选择:

    1. 获取用户或系统设置的真实标签。这可能是“New volume”、“Install (\Server)”、“Contoso Pro Installation disk 4”等等。李>
    2. 获取与资源管理器(我的电脑/这台电脑窗口)中显示的完全一致的标签。这与 (1) 相同,但它遵循在文件夹选项对话框中设置的用户首选项,例如“隐藏驱动器号”。示例:“新卷 (Q:)

    要获得选项 (1) 中说明的信息,您必须使用以下代码:

        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;
        }
    

    对于选项 (2),将 SIGDN.PARENTRELATIVEEDITING 替换为 SIGDN.PARENTRELATIVESIGDN.NORMALDISPLAY

    注意:对于选项2,也有使用ShGetFileInfo()的1-call方法,但它无论如何都会调用这些方法,并且灵活性较差,所以我不在这里发布。

    注意 2:请记住,SHGetNameFromIDList() 的签名在此示例中进行了优化。如果驱动器标签只是临时使用(尤其是不时重新读取),此示例会引入少量内存泄漏。为避免这种情况,请将最后一个参数声明为out IntPtr,然后使用类似

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

    注意 3:这适用于 Windows shell,因此无论此标签的来源如何 - 卷标、用户编辑、Autorun.inf 文件或其他任何内容,它都会返回用户期望的内容。

    【讨论】:

      【解决方案2】:

      感谢有关 autorun.inf 的提示。这是我为检索标签而创建的 C# 片段。

      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
          }
      

      【讨论】:

        【解决方案3】:

        看起来“我的电脑”正在查看 autorun.inf 并使用 [autorun] 部分中的 label= 值。

        仍然不太清楚“DVD RW Drive”和“Floppy Disk Drive”标签的来源,但我猜它们可能是根据驱动器类型进行硬编码的。

        【讨论】:

          【解决方案4】:

          希望以下内容对您有所帮助:

          [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;
              }
          

          【讨论】:

          • 这段代码通常很糟糕,并没有显示 OP 想要看到的内容。从System.IO.DriveInfo.GetDrives() 获得此信息要容易得多。
          【解决方案5】:

          我自己没试过this,但是在注册表里找

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

          然后阅读

          DefaultLabel
          

          键。另外警告!将无效的键/值写入注册表会严重损坏您的系统!在继续之前,请确保您确定自己在做什么。 Here is a resource to help you with accessing the registry programmatically.

          【讨论】:

          • 你有什么操作系统? Win XP Home 中没有名为“DriveIcons”的键!!
          • @Nayan - 我使用的是 Windows 7。
          • 我认为该值可能在注册表中,因此已经搜索“TrueCrypt Traveler Disk”,并在以下位置找到它:HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\{73716ff9-140f -11df-b2ee-0018f30639c7}_Autorun\DefaultLabel 因此,如果该值位于您指定的位置,我可能已经能够检索到该值,但我发现它所在位置的 GUID 会造成问题。
          【解决方案6】:

          This 看起来可能是一个解决方案。

          【讨论】:

          • 谢谢,但 Win32_DiskDrive 和 Win32_PhysicalMedia 似乎也不包含我正在寻找的值。我现在已经尝试了几个 Win32 类,但似乎没有一个!
          【解决方案7】:

          它位于 autorun.inf 文件夹中。我的闪存驱动器的卷标只有 16G, 但是通过放置一个带有以下文本的 autorun.inf 文件 [自动运行] label=我的 16 GB 闪存盘

          然后使用 attrib to +s +h +r 文件,除非我有,否则它不会显示 在文件夹选项/视图下显示隐藏文件和显示系统文件。

          要通过 C# 以编程方式定位它,老实说,我还没有尝试打开 autorun.inf, 但它应该是直截了当的,检查 File.Exists(Drive:\autorun.inf) 是否忽略 它是 +s+h+r 的事实(以防有人设置它),然后以只读方式打开它 并解析 label= 行。如果实际上该文件存在,请使用自动运行标签而不是 卷标。

          即使在 Windows 7 中,我仍然可以使用 autorun.inf label= 标签来修改标签。

          【讨论】:

            【解决方案8】:

            最近我遇到了同样的问题,但我已经能够解决。以下是如何获取在 Windows 资源管理器中显示的标签:

            1. 添加C:\Windows\System32\shell32.dll 作为参考。
            2. 添加using Shell32;
            3. 实例化外壳对象: dynamic shellObject = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
            4. 获取磁盘数据:var driveData = (Folder2)ShellObject.NameSpace(driveInfo.Name);
            5. 参数driveData.Name将包含标签(例如:“Local Disk (C:)”)。

            下面是完整的代码:

            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);
                    }
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2014-10-23
              • 1970-01-01
              • 2021-08-28
              • 1970-01-01
              • 1970-01-01
              • 2012-05-24
              • 1970-01-01
              • 1970-01-01
              • 2020-06-23
              相关资源
              最近更新 更多