Monthly Archives: February 2015
PowerShell: Getting the Disk Drive from a Volume or Mount Point
So this is way harder than I thought it would be.
If you take a look at the WMI class for Win32_MountPoint
You’ll notice there is a remark there that says:
There may not be any way to search from Win32_MountPoint to get to the associated Win32_DiskDrive
Which also applies to the Volume.
That sends you over to this blog post which talks about a win32 api call via DeviceIoControl and I managed to find decent info on PInvoke.net and also this blog post here which basically gives some VB code for it.
So after taking all that in, I was able to write a simple C# snippet that you can toss in PS to query for a Volume
Add-Type -TypeDefinition @"
using System;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Runtime.InteropServices;
public class GetDisk
{
private const uint IoctlVolumeGetVolumeDiskExtents = 0x560000;
[StructLayout(LayoutKind.Sequential)]
public struct DiskExtent
{
public int DiskNumber;
public Int64 StartingOffset;
public Int64 ExtentLength;
}
[StructLayout(LayoutKind.Sequential)]
public struct DiskExtents
{
public int numberOfExtents;
public DiskExtent first;
}
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint IoControlCode,
[MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer,
uint nInBufferSize,
ref DiskExtents OutBuffer,
int nOutBufferSize,
ref uint pBytesReturned,
IntPtr Overlapped
);
public static string GetPhysicalDriveString(string path)
{
//clean path up
path = path.TrimEnd('\\');
if (!path.StartsWith(@"\\.\"))
path = @"\\.\" + path;
SafeFileHandle shwnd = CreateFile(path, FileAccess.Read, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.Open, 0,
IntPtr.Zero);
if (shwnd.IsInvalid)
{
//Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
Exception e = Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
}
var bytesReturned = new uint();
var de1 = new DiskExtents();
bool result = DeviceIoControl(shwnd, IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, ref de1,
Marshal.SizeOf(de1), ref bytesReturned, IntPtr.Zero);
shwnd.Close();
if(result)
return @"\\.\PhysicalDrive" + de1.first.DiskNumber;
return null;
}
}
"@
This will allow you to call this static method to get its Disk path, so you can do something like this.
$DeviceID = [GetDisk]::GetPhysicalDriveString('c:\')
gwmi win32_diskdrive | ? { $_.deviceid -eq $DeviceID}
Now you say, but Justin, this isnt working for a mount point path!? Which is true, but we can work around that.
We can use the Win32_MountPoint class to figure this out, or, you can use this cmdlet Get-MountPointData which basically parses the class for you. Really all you need to do is get the Volume DeviceID, which looks something like this.
Volume{ccbca757-b6c9-11e4-8275-448a5ba2d884}
So we can take that and search on it like so.
[GetDisk]::GetPhysicalDriveString('Volume{78498ddf-9a97-11e4-8252-806e6f6e6963}')
NOTE: This must be run as admin, since we’re hitting the disk subsystem (kernel)
Enjoy!
