Shared/Get-GlobalRootDrives.psm1
Function Get-GlobalRootDrives { <# .SYNOPSIS A function that gets the DriveLetter mappings in the global root namespace And fixes these: \Device\Harddiskvolume .LINK https://superuser.com/questions/1058217/list-every-device-harddiskvolume .INPUTS None. You cannot pipe objects to this function. .OUTPUTS System.Objects[] #> [CmdletBinding()] param () # Importing the $PSDefaultParameterValues to the current session, prior to everything else . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" # Import the kernel32.dll functions using P/Invoke [System.String]$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 the signature to the current session as a new type Add-Type -ErrorAction SilentlyContinue -Language CSharp -MemberDefinition $Signature -Name 'Win32Utils' -Namespace 'PInvoke' -Using PInvoke, System.Text # Initialize some variables for storing the volume names, paths, and mount points [System.UInt32]$lpcchReturnLength = 0 [System.UInt32]$Max = 65535 [System.Text.StringBuilder]$SbVolumeName = New-Object -TypeName System.Text.StringBuilder($Max, $Max) [System.Text.StringBuilder]$SbPathName = New-Object -TypeName System.Text.StringBuilder($Max, $Max) [System.Text.StringBuilder]$SbMountPoint = New-Object -TypeName System.Text.StringBuilder($Max, $Max) # Find the first volume in the system and get a handle to it [System.IntPtr]$VolumeHandle = [PInvoke.Win32Utils]::FindFirstVolume($SbVolumeName, $Max) # Loop through all the volumes in the system do { # Get the volume name as a string [System.String]$Volume = $SbVolumeName.toString() # Get the mount point for the volume, if any [System.Boolean]$unused = [PInvoke.Win32Utils]::GetVolumePathNamesForVolumeNameW($Volume, $SbMountPoint, $Max, [System.Management.Automation.PSReference]$lpcchReturnLength) # Get the device path for the volume, if any [System.UInt32]$ReturnLength = [PInvoke.Win32Utils]::QueryDosDevice($Volume.Substring(4, $Volume.Length - 1 - 4), $SbPathName, [System.UInt32]$Max) # If the device path is found, create a custom object with the drive mapping information if ($ReturnLength) { [System.Collections.Hashtable]$DriveMapping = @{ DriveLetter = $SbMountPoint.toString() VolumeName = $Volume DevicePath = $SbPathName.ToString() } # Write the custom object to the output stream Write-Output -InputObject (New-Object -TypeName PSObject -Property $DriveMapping) } else { # If no device path is found, write a message to the output stream Write-Output -InputObject 'No mountpoint found for: ' + $Volume } # Find the next volume in the system and repeat the loop } while ([PInvoke.Win32Utils]::FindNextVolume([System.IntPtr]$VolumeHandle, $SbVolumeName, $Max)) } # Export external facing functions only, prevent internal functions from getting exported Export-ModuleMember -Function 'Get-GlobalRootDrives' |