Get Volume Path from Drive Name using Powershell script

I have been working with file access events in Clustered File Server. In event log, for every file access, we are getting 4663 and 4656 events with file path like: DeviceHarddiskVolume4MySharetest.txt instead of my local path F:MySharetest.txt. After I have analyzed , found the prefix path DeviceHarddiskVolume4 is mapped with the device F:. Finally, I found this link (http://poshcode.org/4768), it contains powershell script to list Device Name (Drive letter) and mapped Device Path (Volume Path).

List Device Name and Volume Path using Powershell script

We can get the Volume Path from Device Name using the Kernel32 module function QueryDosDevice and we can list the available device names (drive letter) using WMI class Win32_Volume. Use the the below Powershell script to list device path and device name.

   1. Copy the below Powershell script and paste in Notepad file.
   2. Save As the Notepad file with the extension .ps1 like List-Device-Name-and-Path.ps1

Powershell Script: Download List-Device-Name-and-Path.ps1

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

   3. Now run the script file List-Device-Name-and-Path.ps1 from Powershell to list all the Device Name and Volume Path in local machine.

Get Device Path from Device Name using Powershell script

Get Device Path from Device Name (Drive Letter) using Powershell script

The below script displays Device Path for the given Device Name(Derive letter). The device name(drive letter) cannot have a trailing backslash; for example, use “C:“, not “C:“. Follow the below steps to get volume path from drive letter.

   1. Copy the below Powershell script and paste in Notepad file.
   2. Save As the Notepad file with the extension .ps1 like Get-DevicePath-from-DeviceName.ps1

Powershell Script: Download Get-DevicePath-from-DeviceName.ps1

    $driveLetter = Read-Host "Enter Drive Letter:"
    Write-Host " "
    $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)
    $ReturnLength = $Kernel32::QueryDosDevice($driveLetter, $StringBuilder, $Max)

     if ($ReturnLength)
     {
         Write-Host "Device Path: "$StringBuilder.ToString()
      }
      else
      {
          Write-Host "Device Path: not found"
      }
    Write-Host " "
    

   3. Now run the script file Get-DevicePath-from-DeviceName.ps1 from Powershell, and give the Drive letter as argument which you want to get device path.

Get Volume Path from Drive Name using Powershell script

Thanks,
Morgan
Software Developer


Advertisement

2 thoughts on “Get Volume Path from Drive Name using Powershell script”

  1. i tried this first and saw some volume numbers didn’t match up with what was listed in Event Viewer, what does match is:
    diskpart
    list volume

    Reply
  2. the volume numbers shown by DISKPART (when using “list vol”) are NOT the same as hardware volume paths such as “\Device\HarddiskVolume2”.

    DISKPART runs at the Win32 subsystem where volumes are unified and just given ordinal numbers from 1 to N); hardware volume paths are managed by the lower NT lernel level (with individual drivers for buses, EFI, HAL, BCD…) which probes buses and the PlugnPlay system of drivers to detect capabilities and assign a driver instances path with unique GUID in the device manager; it is then the Win32 susbsystem (running on top of the NT kernel) that will use them as mountable volumes unique volume GUID exposed by the volume manager.

    Then DISKPART enumerates these GUID but just expose them as ordinals. The volume GUIDs (for example “\\?\Volume{a686b0f5-8f31-413e-a5d3-bdc5c95bbc07}\”) are then exposed to Win32 once a relevant filesystem driver can be mapped on them (using FS type byte in MBR partition, or GUID partition on GPT disks) and remapped for virtual disks, dynamic disks and RAID volumes, encryption manager like Bilocker…). At this step, mountable volumes have a volume GUID for Win23 and it is used as long as volumes are not mounted and not assigned a drive letter (you can see them with the “VOLMOUNT” command line tool). The drive letter is just a shortcut for “\\?\Volume{a686b0f5-8f31-413e-a5d3-bdc5c95bbc07}\”, compatible with legacy DOS and easier to manager for users in the UI.

    But you are not required to use a volume with a drive letter as you can mount them in a directory, or directly in Win32 without any letter when using the “\\?\” prefix, allowing Win32 apps to use more than 26 drive letters, or virtualized environments or containers to use different drive letters for the same volume.

    —-
    So the Powershell scripts above are useful to show the effective mapping between devices paths and drive letters (when volumes are mounted): in the Windows registry (such as the device manager and hivelist) you need to specify the correct device path used as the lower level.

    Unfortunately, basic Windows command line tools like DISKPART do not expose this mapping (it should be listed when using “SELECT VOLUME n” then “DETAIL VOLUME” or “DETAIL PARTITION” to return giving the two paths “\Device\HarddiskVolumeN” (lower NT HAL level) and “”\\?\Volume{a686b0f5-8f31-413e-a5d3-bdc5c95bbc07}\” (upper Win32 level). Note that hardware volumes may not necessarily map to a volumeGUID (notably when hardware volumes are broken and can’t be deciphered by the volume manager (which assigns the volumeGUIDs in its own database, independant from the much more complex database used by the device manager and the specific local databased used by various filesystem handler drivers and storage adaptation drivers).

    Reply

Leave a Comment