在C#中获取驱动器标签

14
当我使用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:)
DVD光驱 (D:)
TrueCrypt Traveler 磁盘 (E:)
备份 (Y:)
数据 (Z:)
8个回答

6
很遗憾,要想获取这些信息而不使用黑客和奇怪的技巧,您需要使用P/Invoke技术。有两个选项:
  1. 获取用户或系统设置的真实标签。这可以是“New volume”,“Install (\Server)”,“Contoso Pro Installation disk 4”等。
  2. 准确地获取在资源管理器(My computer / This PC window)中显示的标签。 这与(1)相同,但它遵循文件夹选项对话框中设置的用户首选项,例如“隐藏驱动器号”。示例:“New volume(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次调用方法,但它无论如何都会调用这些方法,并且不够灵活,因此我在此不予发布。
注意2:请记住,此示例中SHGetNameFromIDList()的签名已经被优化。如果仅暂时使用驱动器标签(特别是如果它不时重新读取),则此示例会引入小的内存泄漏。为避免这种情况,请将最后一个参数声明为out IntPtr,然后使用类似以下内容的语句。
     var tmp = Marshal.PtrToStringUni(ppszName);
     Marshal.FreeCoTaskMem(ppszName);

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


3
感谢您提供有关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
    }

1
我希望以下内容对你有所帮助:
[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()中获取这些信息会更加容易。 - GSerg

1

看起来我的电脑查看autorun.inf文件,并使用[autorun]部分中的label=值。

仍然不太确定“DVD RW驱动器”和“软盘驱动器”的标签来自哪里,但我猜它们可能是基于驱动器类型硬编码的。


0

我自己没有尝试过this,但是在注册表中搜索

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

然后读取

DefaultLabel

键。此外,警告!向注册表中写入无效的键/值可能会严重损坏您的系统!在继续之前,请确保您确定自己正在做什么。这里有一个资源可以帮助您以编程方式访问注册表。


你用的是什么操作系统?在Win XP Home中没有名为“DriveIcons”的键!! - Nayan
@Nayan - 我正在使用Windows 7。 - Bob Kaufman
我认为该值可能在注册表中,因此已经搜索了“TrueCrypt Traveler Disk”,并在以下位置找到它:HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2{73716ff9-140f-11df-b2ee-0018f30639c7}_Autorun\DefaultLabel因此,如果该值位于您指定的位置,则可能已经能够检索该值,但是我在找到它的位置中的GUID使事情变得棘手。 - Rick

0

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


谢谢,但Win32_DiskDrive和Win32_PhysicalMedia似乎也没有我要查找的值。我现在已经尝试了几个Win32类,但似乎都不行! - Rick

0

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

然后使用attrib +s +h +r命令对该文件进行隐藏,除非我启用了 文件夹选项/视图下的显示隐藏文件和显示系统文件。

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

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


0

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

  1. C:\Windows\System32\shell32.dll添加为引用。
  2. 添加using Shell32;
  3. 实例化shell对象:dynamic shellObject = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
  4. 获取磁盘数据:var driveData = (Folder2)ShellObject.NameSpace(driveInfo.Name);
  5. 参数driveData.Name将包含标签(例如:“本地磁盘(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);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接