Mounting VHDs from Managed Code
When Virtual Server 2005 R2 SP1 was released it included the VHDMount tool - that came with a DLL to allow you to control it programmatically. Unfortunately we were rather light with the documentation / sample code for this. So today I would like to show you how to drive VHD mounting from managed code.
To do this I will be creating a small application called 'VHDMounter'. It will have a simple user interface like this:
To help with tracking things in the code I have named the TextBox "VHDTextBox" and the buttons "MountButton" and "UnmountButton" respectively. Once the basic user interface is built here is the code that you need to drive the application:
VB:
Imports System.Runtime.InteropServices
Public Class Form1
'Setup the VHD_FLAGS enumeration. Data copied from VHDMount.h
Enum VHD_FLAGS
VHD_NORMAL
VHD_NW_MAPPED ' Unused
VHD_MOUNT_AS_READONLY ' Unused
VHD_FORCE_UNMOUNT
End Enum
'Create interop for the MountVHD call off of vhdmount.dll
<DllImport("C:\\Program Files\\Microsoft Virtual Server\\Vhdmount\\vhdmount.dll", CharSet:=CharSet.Auto)> _
Public Shared Function MountVHD(ByVal VHDFileName As String, ByVal Flags As Integer) As Integer
End Function
'Create interop for the MountVHD call off of vhdmount.dll
<DllImport("C:\\Program Files\\Microsoft Virtual Server\\Vhdmount\\vhdmount.dll", CharSet:=CharSet.Auto)> _
Public Shared Function UnmountVHD(ByVal VHDFileName As String, ByVal Flags As Integer) As Integer
End Function
'Mount the selected VHD when the user clicks on the MountButton
Private Sub MountButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MountButton.Click
Dim result As Integer
'Call MountVHD with the parameter of the text in the text box
result = MountVHD(VHDTextBox.Text, VHD_FLAGS.VHD_NORMAL)
'Handle result code
If result = 0 Then
MsgBox(Chr(34) & VHDTextBox.Text & Chr(34) & " was successfully mounted.")
Else
MsgBox("An error was encountered attempting to mount " & Chr(34) & VHDTextBox.Text & Chr(34) & "." & Chr(10) & Chr(10) & "The error code returned was: " & result & ".")
End If
End Sub
'Unmount the selected VHD when the user clicks on the UnmountButton
Private Sub UnmountButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles UnmountButton.Click
Dim result As Integer
'Call UnmountVHD with the parameter of the text in the text box
result = UnmountVHD(VHDTextBox.Text, VHD_FLAGS.VHD_NORMAL)
'Handle result code
If result = 0 Then
MsgBox(Chr(34) & VHDTextBox.Text & Chr(34) & " was successfully unmounted.")
Else
MsgBox("An error was encountered attempting to unmount " & Chr(34) & VHDTextBox.Text & Chr(34) & "." & Chr(10) & Chr(10) & "The error code returned was: " & result & ".")
End If
End Sub
End Class
C#:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace VHDMount_C
{
public partial class Form1 : Form
{
// Setup the VHD_FLAGS enumeration. Data copied from VHDMount.h
enum VHD_FLAGS
{
VHD_NORMAL,
VHD_NW_MAPPED, // Unused
VHD_MOUNT_AS_READONLY, // Unused
VHD_FORCE_UNMOUNT
} ;
// Create interop for the MountVHD call off of vhdmount.dll
[DllImport("C:\\Program Files\\Microsoft Virtual Server\\Vhdmount\\vhdmount.dll", CharSet = CharSet.Auto)]
static extern UInt32 MountVHD(String VHDFileName, UInt32 Flags);
// Create interop for the MountVHD call off of vhdmount.dll
[DllImport("C:\\Program Files\\Microsoft Virtual Server\\Vhdmount\\vhdmount.dll", CharSet = CharSet.Auto)]
static extern UInt32 UnmountVHD(String VHDFileName, UInt32 Flags);
public Form1()
{
InitializeComponent();
}
// Mount the selected VHD when the user clicks on the MountButton
private void MountButton_Click(object sender, EventArgs e)
{
UInt32 result;
// Call MountVHD with the parameter of the text in the text box
result = MountVHD(VHDTextBox.Text, (UInt32)VHD_FLAGS.VHD_NORMAL);
// Handle result code
if (result == 0)
MessageBox.Show((char)34 + VHDTextBox.Text + (char)34 + " was successfully mounted.");
else
MessageBox.Show("An error was encountered attempting to mount " + (char)34 + VHDTextBox.Text + (char)34 + "." + (char)10 + (char)10 + "The error code returned was: " + result + ".");
}
// Unmount the selected VHD when the user clicks on the UnmountButton
private void UnmountButton_Click(object sender, EventArgs e)
{
UInt32 result;
// Call UnmountVHD with the parameter of the text in the text box
result = UnmountVHD(VHDTextBox.Text, (UInt32)VHD_FLAGS.VHD_NORMAL);
// Handle result code
if (result == 0)
MessageBox.Show((char)34 + VHDTextBox.Text + (char)34 + " was successfully unmounted.");
else
MessageBox.Show("An error was encountered attempting to unmount " + (char)34 + VHDTextBox.Text + (char)34 + "." + (char)10 + (char)10 + "The error code returned was: " + result + ".");
}
}
}
Let us have a dig into what is happening here:
The first thing to do is to create an enum for VHD_FLAGS. This information is grabbed directly from the VHDMount.h file in the vhdmount directory from where Virtual Server is installed.
Next you need to create interop code for 'MountVHD' and 'UnmountVHD' on vhdmount.dll. Note that while I am doing a full path reference to the copy of vhdmount.dll that is included with my installation of Virtual Server - you probably just want to make a local copy of this file for your project (also note that the Virtual Server EULA does grant you redistribution rights if you want to include this in a formal product).
Finally hookup some basic code for click events on MountButton and UnmountButton to try and mount / unmount the virtual hard disk specified in VHDTextBox. In this code I do not do any checking on the text that I am passing to vhdmount.dll because I am lazy and vhdmount.dll does a very good job of checking the validity of the string anyway. The result code will always be '0' if the operation is successful. Any other result code means a failure of some kind. To look up the meaning of a result code go to here: https://msdn2.microsoft.com/en-us/library/ms681382(VS.85).aspx
Two final things to be aware of:
This code was written using Visual Studio 2008 - I make no guarantees of compatibility with other versions of Visual Studio.
In order for this code to work it needs to be running with Administrative privilege. You can manually launch with administrative privilege - or you can manifest it to require administrative privilege. You can read how to do this with Visual Studio 2008 here: https://www.danielmoth.com/Blog/2007/08/uac-settings-in-vb.html
Cheers,
Ben