22

I've tried Diskpart commands like "list" "volume" (no it's not that at all), "disk" and "partition"; but it still don't work.

\Device\Harddiskvolume0 seems to not be used, since \Device\Harddiskvolume1 means the first Windows' partition (aka "System Reserved") and \Device\Harddiskvolume2 is for C:.

So the question is: How to list every \Device\Harddiskvolume in Windows' 7 installation disk (for BCD editing) ?

X.LINK
  • 2,291
  • 5
  • 23
  • 33

7 Answers7

17

I adapted @merle's answer by using the approach documented on MSDN.

It shows drives:

  • without drive letters
  • mounted to a folder
  • with drive letters

Sample output:

DriveLetter                    DevicePath               VolumeName                                       
-----------                    ----------               ----------                                       
                               \Device\HarddiskVolume5  \\?\Volume{a2b4c6d8-0000-0000-00000100000000000}\
E:\                            \Device\HarddiskVolume9  \\?\Volume{a2b4c6d8-1234-1234-1234-123456789abc}\
C:\Mounted\My-Folder-Mount\    \Device\HarddiskVolume13 \\?\Volume{a2b4c6d8-1234-1234-1234-123456789abc}\

PowerShell script:

$signature = @'
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetVolumePathNamesForVolumeNameW([MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeName,
        [MarshalAs(UnmanagedType.LPWStr)] [Out] StringBuilder lpszVolumeNamePaths, uint cchBuferLength, 
        ref UInt32 lpcchReturnLength);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr FindFirstVolume([Out] StringBuilder lpszVolumeName,
   uint cchBufferLength);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FindNextVolume(IntPtr hFindVolume, [Out] StringBuilder lpszVolumeName, uint cchBufferLength);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);

'@;
Add-Type -MemberDefinition $signature -Name Win32Utils -Namespace PInvoke -Using PInvoke,System.Text;

[UInt32] $lpcchReturnLength = 0;
[UInt32] $Max = 65535
$sbVolumeName = New-Object System.Text.StringBuilder($Max, $Max)
$sbPathName = New-Object System.Text.StringBuilder($Max, $Max)
$sbMountPoint = New-Object System.Text.StringBuilder($Max, $Max)
[IntPtr] $volumeHandle = [PInvoke.Win32Utils]::FindFirstVolume($sbVolumeName, $Max)
do {
    $volume = $sbVolumeName.toString()
    $unused = [PInvoke.Win32Utils]::GetVolumePathNamesForVolumeNameW($volume, $sbMountPoint, $Max, [Ref] $lpcchReturnLength);
    $ReturnLength = [PInvoke.Win32Utils]::QueryDosDevice($volume.Substring(4, $volume.Length - 1 - 4), $sbPathName, [UInt32] $Max);
    if ($ReturnLength) {
           $DriveMapping = @{
               DriveLetter = $sbMountPoint.toString()
               VolumeName = $volume
               DevicePath = $sbPathName.ToString()
           }

           Write-Output (New-Object PSObject -Property $DriveMapping)
       }
       else {
           Write-Output "No mountpoint found for: " + $volume
       } 
} while ([PInvoke.Win32Utils]::FindNextVolume([IntPtr] $volumeHandle, $sbVolumeName, $Max));
phuclv
  • 26,555
  • 15
  • 113
  • 235
phant0m
  • 220
  • 2
  • 9
  • 4
    Nice! A few other useful outputs to add might be the FileSystemLabel (aka FriendlyName), Size, and perhaps the corresponding disk and partition numbers. For my hacky one-off purposes, I just tacked these two lines on to the end of your script, so I could manually match the Volume GUIDs. `Get-Volume | Select DriveLetter, FileSystemLabel, FileSystemType, Size, Path | Format-Table -Autosize` and with a new line `Get-Partition | Select DiskNumber, PartitionNumber, AccessPaths, Size | Sort-Object -Property DiskNumber, PartitionNumber | Format-Table -Autosize` – Ryan Feeley Apr 29 '20 at 22:23
9

Found a powershell script that lists the mounted volumes:

# Biuild System Assembly in order to call Kernel32:QueryDosDevice. 
   $DynAssembly = New-Object System.Reflection.AssemblyName('SysUtils')
   $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
   $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('SysUtils', $False)
 
   # Define [Kernel32]::QueryDosDevice method
   $TypeBuilder = $ModuleBuilder.DefineType('Kernel32', 'Public, Class')
   $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('QueryDosDevice', 'kernel32.dll', ([Reflection.MethodAttributes]::Public -bor [Reflection.MethodAttributes]::Static), [Reflection.CallingConventions]::Standard, [UInt32], [Type[]]@([String], [Text.StringBuilder], [UInt32]), [Runtime.InteropServices.CallingConvention]::Winapi, [Runtime.InteropServices.CharSet]::Auto)
   $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
   $SetLastError = [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
   $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor, @('kernel32.dll'), [Reflection.FieldInfo[]]@($SetLastError), @($true))
   $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)
   $Kernel32 = $TypeBuilder.CreateType()
 
   $Max = 65536
   $StringBuilder = New-Object System.Text.StringBuilder($Max)
 
   Get-WmiObject Win32_Volume | ? { $_.DriveLetter } | % {
       $ReturnLength = $Kernel32::QueryDosDevice($_.DriveLetter, $StringBuilder, $Max)
 
       if ($ReturnLength)
       {
           $DriveMapping = @{
               DriveLetter = $_.DriveLetter
               DevicePath = $StringBuilder.ToString()
           }
 
           New-Object PSObject -Property $DriveMapping
       }
   }

Source: http://www.morgantechspace.com/2014/11/Get-Volume-Path-from-Drive-Name-using-Powershell.html

Output looks like this:

DevicePath               DriveLetter
----------               -----------
\Device\HarddiskVolume2  F:         
\Device\HarddiskVolume7  J:         
\Device\HarddiskVolume10 D:         
\Device\HarddiskVolume12 E:         
\Device\HarddiskVolume5  C:    

 
phuclv
  • 26,555
  • 15
  • 113
  • 235
merle
  • 121
  • 1
  • 3
  • 2
    Unfortunately, this does not list the volumes that aren't mounted under a drive letter. – phant0m Feb 01 '19 at 15:07
  • Here is my adapted script that shows *all* drives, no matter whether folder mounted or no drive letter at all: https://superuser.com/a/1401025/59487 – phant0m Dec 02 '19 at 11:06
5

The reason I couldn't get things done is that HarddiskVolume doesn't reflect Diskpart volumes -which only lists every Windows readable volumes-.

In fact, it works with every partitions available on the disk -even the non-Windows ones-, by order they appear like in Linux's Gparted.

E.g, if you have an sda4 before sda3, this latter will show as is -sda4 then sda3- (HarddiskVolume4 then HarddiskVolume3).

So, it means that HarddiskVolume0 mainly don't exist in BCD.

The commands that helped me to understand that are:

mountvol /L

bootsect /nt60 all /force   ->   Be careful with that one !!!

These links also helped me:

Finally, if you have a spare Windows, just run DriveLetterView to see how Windows works with HarddiskVolume.

Note: The HarddiskVolume is a WMI/COM notation

X.LINK
  • 2,291
  • 5
  • 23
  • 33
  • If you can install 3rd party tools, DriveLetterView from the venerable Nirsoft, as mentioned here, is by far the easiest, in this humble tech's opinion. Gives you disk numbers and volume numbers, volume drive letters, and everything else (vendor, disk name, volume name, disk space, etc) all in one nice easy view, saveable to disk. – john v kumpf Dec 23 '20 at 04:17
5

The easiest way without installing anything and tinkering with Powershell scripts might be System Information Viewer a portable Windows application. This app is great because it provides nearly every information about your machine / hardware. It not only offers a read out of hard drive related data rather nearly everything about your device can be found. Moreover it's very lightweight but TBH a bit confusing structured.

Finally, how do you find drive information? Under Volumes ▼ there is the option Volume List that will give you an overview of all \Device\HarddiskvolumeXX present on your computer. Additionally you get drive letter and GUID of your partitions.

To list all \Device\HarddiskVolumeXX including those that are not mounted under any drive letter per physical driver together with the disk number (like seen in Windows Disk Management). Open the Volumes ▼ dropdown and choose Disk Mapping.

I also want to highlight the option Drives which displays \.\PhysicalDriveXX, path, unit and controller IDs. The listing under Drive Mapping might also be quite useful.

thex
  • 311
  • 3
  • 8
0

If you want just to find out where your system BCD store is take a look at \REGISTRY\MACHINE\BCD00000000 value in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\hivelist registry key. Yes, its location can differ from \Device\HardDiskVolume1 even if it is on the 1st partition of the 1st physical disk.

ᄂ ᄀ
  • 3,875
  • 1
  • 19
  • 18
GCRaistlin
  • 99
  • 1
  • 8
0

An easier way to do it is as written below. I've also customized a couple of the columns.

Please run the below in PowerShell:

Get-CimInstance win32_volume -ComputerName "Enter Your Computer Name, or Multiple Computer Names" | select @{n="ComputerName";e={$_.PSComputerName}},DriveLetter,@{n="Capacity(GB)";e={$_.Capacity / 1gb -as [int]}},@{n="Free(GB)";e={$_.FreeSpace / 1gb -as [int]}} | ft -AutoSize
Run5k
  • 15,723
  • 24
  • 49
  • 63
  • 1
    This is for PowerShell v3 only, user specified for Windows 7 which doesn't come with v3 by default. – JasonXA Mar 29 '19 at 04:38
0

AFAIK a universal solution is to use QueryDosDevice from Win32 API.

Short version:

$Kernel32 = Add-Type -Name 'Kernel32' -Namespace '' -PassThru -MemberDefinition @"
    [DllImport("kernel32")]
    public static extern int QueryDosDevice(string name, System.Text.StringBuilder path, int pathMaxLength);
"@

$DevicePath = New-Object System.Text.StringBuilder(255)
$GetDevicePath = {$Kernel32::QueryDosDevice( `
  $_.UniqueId.TrimStart('\\?\').TrimEnd('\'), $DevicePath, $DevicePath.Capacity) | Out-Null; $DevicePath}

Get-Volume | ft DriveLetter, FileSystemLabel, @{n='DevicePath';e=$GetDevicePath}, UniqueId

More verbose and error-prone version:

$Kernel32 = Add-Type -Name 'Kernel32' -Namespace '' -PassThru -MemberDefinition @"
    [DllImport("kernel32", SetLastError = true)]
    public static extern int QueryDosDevice(string name, System.Text.StringBuilder path, int pathMaxLength);
"@

$DevicePath = New-Object System.Text.StringBuilder(255)
Get-Volume | % {
    if ($_.UniqueId -match '(Volume\{.*\})') {
        $VolumeName = $Matches[1]
    } else {
        Write-Host "Unable to lookup volume name in '$($_.UniqueId)'"
        Return
    }
    $ReturnLength = $Kernel32::QueryDosDevice($VolumeName, $DevicePath, $DevicePath.Capacity)
    if ($ReturnLength) {
        [PSCustomObject]@{
            DriveLetter = $_.DriveLetter
            Label = $_.FileSystemLabel
            FileSystem = $_.FileSystem
            "Size (GB)" = $_.Size / 1GB
            "Free (GB)" = $_.SizeRemaining / 1GB
            DevicePath = $DevicePath.ToString()
            Id = $_.UniqueId
        } 
    } else {
        $errorCode = [System.Runtime.InteropServices.Marshal]::GetLastPInvokeError()
        Write-Host "Unable to query DOS device by '$VolumeName'." (New-Object System.ComponentModel.Win32Exception($errorCode)).Message
    }
} | ? { $_.DevicePath } | Sort-Object DevicePath | ft
ᄂ ᄀ
  • 3,875
  • 1
  • 19
  • 18