It turns out the easiest part was opening and attaching the VHD file. The VHD APIs are pretty straight-forward, and a simple managed wrapper lets you call these from C#. (I chose a Managed C++ wrapper, but there is a C# wrapper here)
By default AttachVirtualDisk will mount all the volumes on the virtual disk, which is what I wanted. (You could also tell the VHD API to not mount them and do it yourself for the partitions you cared about, but that sounded like a lot of extra work to me...)
Now came the fun - which drive letters were assigned to the mounted partitions on the VHD? It turns out the VHD API doesn't provide this information - the most it will give you is the physical path for the drive device (see GetVirtualDiskPhysicalPath)
I looked around and found some examples of people using Powershell to mount VHDs and figure out the drive letters. Some folks were using the Virtual Disk Service (VDS), which has a managed wrapper in Server 2008 (Microsoft.Storage.Vds.dll) - Taylor Brown talks about this on his blog.
That wouldn't work on Windows 7 though, so I was left considering writing a managed wrapper for the VDS COM interface. My head was beginning to hurt... :)
Luckily I found some hints at using WMI to get the info - using WMI from .NET is not too complicated once you figure out which objects and queries to use. Here's the resulting code (Managed C++) which retrieves the logical drive for the first partition on the VHD:
// Return the logical disk for the VHD's first partition
property String^ LogicalDisk
{
String^ get()
{
String^ physicalPath = this->PhysicalPath; // Calls GetVirtualDiskPhysicalPath API wrapper
String^ logicalDisk = nullptr;
// Use WMI to get the logical drive for the first partition on the VHD disk
RelatedObjectQuery^ q = gcnew RelatedObjectQuery(String::Format("\\\\.\\root\\cimv2:Win32_DiskDrive.DeviceID='{0}'", physicalPath), "Win32_DiskPartition");
ManagementObjectSearcher^ searcher = gcnew ManagementObjectSearcher(q);
String^ firstPartition = nullptr;
for each (ManagementObject^ o in searcher->Get())
{
firstPartition = o->Path->ToString();
break;
}
if (firstPartition != nullptr)
{
// Now see which volumes are related to the partitions
q = gcnew RelatedObjectQuery(firstPartition, "Win32_LogicalDisk");
searcher->Query = q;
for each (ManagementObject^ o in searcher->Get())
{
logicalDisk = o->GetPropertyValue("Name")->ToString();
break;
}
}
return logicalDisk;
}
}
2 comments:
The link to the c# wrapper is no longer valid. Do you have another link or can you post it here?
Hi, looks like the blog I linked to for the C# wrapper has moved to jmedved.com.
I updated the link to go to the new site.
Post a Comment