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