Getting a thumbnail from Hyper-V or SCVMM in C#

   This blog post will discuss how to get a thumbnail from Hyper-V. I will show you how to set the size for the thumbnail and save the thumbnail as a usable image file. In this example I will be saving the thumbnail image as a .jpg. There are other blog posts that discuss how to get the thumbnail, and posts on how to work with the System.Management classes to save the thumbnail, but this post will put the parts together in it's entirety and demonstrate how to get the thumbnail as a usable image in C#.

    I've used some code from Taylor Brown's Blog post and such I want to give him due credit. His blogs in my opinion are cutting edge for working with SCVMM via code and have proven invaluable to me in my SCVMM development projects. The post I am referencing can be found here:

https://blogs.msdn.com/taylorb/archive/2008/07/29/hyper-v-wmi-creating-a-thumbnail-image.aspx

    Let's talk about a few things with my blog post. First you can call into SCVMM to get thumbnails.

    We could call into SCVMM and get a thumbnail in powershell but I find that the performance is not nearly as fast as calling directly into Hyper-V. Performance for me is paramount because I am going to be refreshing my thumbnails often. I also prefer not to have to queue many SCVMM cmdlets, I need SCVMM available and ready to service my VMs for actions such as Stop-VM or Remove-VM. Instead I am going to call directly into Hyper-V via WMI calls. Use WMI calls with caution, and you should be comfortable and have a working understanding of WMI calls to fully understand this blog post.

    The function I am going to write, GetThumbnailForVM takes two parameters:

    vmName - string which is the name of the VM we want to get the thumbnail for.

    vmHost - string which tells us what host the VM lives on

    You could dynamically get the vmHost by calling:

Get-VM [vmName] | select VMHost

You could then simply your function to only need vmName.

For more information on creating functions that call into SCVMM from C# and getting these return values to use in your code, see my earlier blog posts.

One last note. If the VM in question is stopped, then you will get a blank screen returned as your image. Makes sense; since there is nothing to return. To keep your program clean you may wish to check the status of the vm, if it is stopped, return an image that gives the user some insight that the VM is in a stopped state. I'll implement this in my code sample to show you how to do this.

Let's take a look at the GetThumbnailforVM function:

public bool GetThumbnailForVM(string vmName, string vmHost)

{

if (vmName.Status == "Stopped")

{

string stopinmage = path + "Images\\Icons\\poweroff.jpg";

string copyimage = path + "Thumbnails\\" + vmName + ".jpg";

File.Delete(copyimage);

File.Copy(stopinmage, copyimage);

return true;

}

Thumbnail thumbnail = new Thumbnail();

thumbnail.GetVirtualSystemThumbnailImage(vmName, vmHost);

return true;

}

Pretty straight forward. I am assuming here that path is something relevant to you. The "path" variable will be set by you referring to where you want to store your VM thumbnails. In this function if the status is stopped, I then go and grab an image I call "poweroff.jpg" and store that image in our path. Since we don't need to get a real thumbnail, I return out of the function here. When I save the thumbnail image, I save it using [vmName] .jpg.

If the VM is not stopped, then I create a new Thumbnail class and call the GetVirtualSystemThumbnailImage method on the Thumbnail class.

Here is the listing for the Thumbnail class. I will break down the things in this class after this listing:

public class Thumbnail

{

static int x = 320, y = 240;

public void GetVirtualSystemThumbnailImage(string vmName, string host)

{

ManagementScope scope = new ManagementScope(@"\\" + host + @"\root\virtualization", null);

ManagementObject virtualSystemService = Utility.GetServiceObject(scope, "Msvm_VirtualSystemManagementService");

ManagementObject vm = Utility.GetTargetComputer(vmName, scope);

ManagementObjectCollection vmsettingDatas = vm.GetRelated(

"Msvm_VirtualSystemsettingData",

"Msvm_SettingsDefineState",

null,

null,

"SettingData",

"ManagedElement",

false,

null);

ManagementObject settingData = null;

foreach (ManagementObject data in vmsettingDatas)

{

settingData = data;

break;

}

ManagementBaseObject inParams = virtualSystemService.GetMethodParameters("GetVirtualSystemThumbnailImage");

inParams["HeightPixels"] = 240;

inParams["WidthPixels"] = 320;

inParams["TargetSystem"] = settingData.Path.Path;

ManagementBaseObject outParams = virtualSystemService.InvokeMethod("GetVirtualSystemThumbnailImage", inParams, null);

if ((UInt32)outParams["ReturnValue"] == ReturnCode.Started)

{

if (Utility.JobCompleted(outParams, scope))

{

SaveImageData((byte[])outParams["ImageData"], vmName);

}

else

{

// Something went wrong with the creation

}

}

else if ((UInt32)outParams["ReturnValue"] == ReturnCode.Completed)

{

SaveImageData((byte[])outParams["ImageData"], vmName);

}

else

{

// Something went wrong with the creation

//ErrorEventArgs =(UInt32)outParams["ReturnValue"]

}

inParams.Dispose();

outParams.Dispose();

settingData.Dispose();

vm.Dispose();

virtualSystemService.Dispose();

}

static void SaveImageData(byte[] imageData, string vmName)

{

path += @"\Thumbnails\";

Bitmap VMThumbnail = new Bitmap(x, y, PixelFormat.Format16bppRgb565);

Rectangle rectangle = new Rectangle(0, 0, x, y);

BitmapData VMThumbnailBiltmapData = VMThumbnail.LockBits(rectangle, ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb565);

System.Runtime.InteropServices.Marshal.Copy(imageData, 0, VMThumbnailBiltmapData.Scan0, x * y * 2);

VMThumbnail.UnlockBits(VMThumbnailBiltmapData);

VMThumbnail.Save(path + vmName + ".jpg");

}

}

We will need to add a reference to the System.Management .dll. We will also need to add using statements for the following namespaces:

using System.Management;

using System.Drawing;

using System.IO;

using System.Drawing.Imaging;

The first area we will look at is the two integers x and y. These numbers represent the size in pixels that we want our thumbnail image to displayed in. You can see that I create a image based on the hard coded values 240 x 320. If you wish, you can make this dyncamic and change these static integers to be properties of the Thumbnail class, so that you can set the size when you instantiate your Thumbnail class.

There are only two methods of this class:

GetVirtualSystemThumbnailImage - taking in the values from earlier, a string for the vmName and a string for the VMhost the VM lives on.

SaveImageData - this is a private method that we use in the GetVirtualSystemThumbnailImage method to save our image data.

When we called our original function we already had the path of where we want to strore the images defined. This path is used again in the SaveImageData function. We do append the path with " \Thumbnails\ " for our store just to make final findings eaiser. As mentioned earlier, you may wish to make this dynamic as well. For that I would simply add a public property to the Thumbnail class that accepts a string value of the path you wish to use.

There is one other class that we use in the GetVirtualSystemThumbnailImage function. I did not write this classl, and thus I will not be going into detail about this class. The class is documented in the following MSDN topic:

Common Utilities for the Virtualization Samples
https://msdn.microsoft.com/en-us/library/cc723869(VS.85).aspx

I will add this class defintion to the bottom of this post, in case this link ever changes.

I hope this saves you some time, and as always enjoy!

static class ResourceType

{

public const UInt16 Other = 1;

public const UInt16 ComputerSystem = 2;

public const UInt16 Processor = 3;

public const UInt16 Memory = 4;

public const UInt16 IDEController = 5;

public const UInt16 ParallelSCSIHBA = 6;

public const UInt16 FCHBA = 7;

public const UInt16 iSCSIHBA = 8;

public const UInt16 IBHCA = 9;

public const UInt16 EthernetAdapter = 10;

public const UInt16 OtherNetworkAdapter = 11;

public const UInt16 IOSlot = 12;

public const UInt16 IODevice = 13;

public const UInt16 FloppyDrive = 14;

public const UInt16 CDDrive = 15;

public const UInt16 DVDdrive = 16;

public const UInt16 Serialport = 17;

public const UInt16 Parallelport = 18;

public const UInt16 USBController = 19;

public const UInt16 GraphicsController = 20;

public const UInt16 StorageExtent = 21;

public const UInt16 Disk = 22;

public const UInt16 Tape = 23;

public const UInt16 OtherStorageDevice = 24;

public const UInt16 FirewireController = 25;

public const UInt16 PartitionableUnit = 26;

public const UInt16 BasePartitionableUnit = 27;

public const UInt16 PowerSupply = 28;

public const UInt16 CoolingDevice = 29;

public const UInt16 DisketteController = 1;

}

static class ResourceSubType

{

public const string DisketteController = null;

public const string DisketteDrive = "Microsoft Synthetic Diskette Drive";

public const string ParallelSCSIHBA = "Microsoft Synthetic SCSI Controller";

public const string IDEController = "Microsoft Emulated IDE Controller";

public const string DiskSynthetic = "Microsoft Synthetic Disk Drive";

public const string DiskPhysical = "Microsoft Physical Disk Drive";

public const string DVDPhysical = "Microsoft Physical DVD Drive";

public const string DVDSynthetic = "Microsoft Synthetic DVD Drive";

public const string CDROMPhysical = "Microsoft Physical CD Drive";

public const string CDROMSynthetic = "Microsoft Synthetic CD Drive";

public const string EthernetSynthetic = "Microsoft Synthetic Ethernet Port";

//logical drive

public const string DVDLogical = "Microsoft Virtual CD/DVD Disk";

public const string ISOImage = "Microsoft ISO Image";

public const string VHD = "Microsoft Virtual Hard Disk";

public const string DVD = "Microsoft Virtual DVD Disk";

public const string VFD = "Microsoft Virtual Floppy Disk";

public const string videoSynthetic = "Microsoft Synthetic Display Controller";

}

static class OtherResourceType

{

public const string DisketteController = "Microsoft Virtual Diskette Controller";

}

static class ReturnCode

{

public const UInt32 Completed = 0;

public const UInt32 Started = 4096;

public const UInt32 Failed = 32768;

public const UInt32 AccessDenied = 32769;

public const UInt32 NotSupported = 32770;

public const UInt32 Unknown = 32771;

public const UInt32 Timeout = 32772;

public const UInt32 InvalidParameter = 32773;

public const UInt32 SystemInUser = 32774;

public const UInt32 InvalidState = 32775;

public const UInt32 IncorrectDataType = 32776;

public const UInt32 SystemNotAvailable = 32777;

public const UInt32 OutofMemory = 32778;

}

class Utility

{

static class JobState

{

public const UInt16 New = 2;

public const UInt16 Starting = 3;

public const UInt16 Running = 4;

public const UInt16 Suspened = 5;

public const UInt16 ShuttingDown = 6;

public const UInt16 Completed = 7;

public const UInt16 Terminated = 8;

public const UInt16 Killed = 9;

public const UInt16 Exception = 10;

public const UInt16 Service = 11;

}

/// <summary>

/// Common utility function to get a service oject

/// </summary>

/// <param name="scope"></param>

/// <param name="serviceName"></param>

/// <returns></returns>

public static ManagementObject GetServiceObject(ManagementScope scope, string serviceName)

{

scope.Connect();

ManagementPath wmiPath = new ManagementPath(serviceName);

ManagementClass serviceClass = new ManagementClass(scope, wmiPath, null);

ManagementObjectCollection services = serviceClass.GetInstances();

ManagementObject serviceObject = null;

foreach (ManagementObject service in services)

{

serviceObject = service;

}

return serviceObject;

}

public static ManagementObject GetHostSystemDevice(string deviceClassName, string deviceObjectElementName, ManagementScope scope)

{

string hostName = System.Environment.MachineName;

ManagementObject systemDevice = GetSystemDevice(deviceClassName, deviceObjectElementName, hostName, scope);

return systemDevice;

}

public static ManagementObject GetSystemDevice

(

string deviceClassName,

string deviceObjectElementName,

string vmName,

ManagementScope scope)

{

ManagementObject systemDevice = null;

ManagementObject computerSystem = Utility.GetTargetComputer(vmName, scope);

ManagementObjectCollection systemDevices = computerSystem.GetRelated

(

deviceClassName,

"Msvm_SystemDevice",

null,

null,

"PartComponent",

"GroupComponent",

false,

null

);

foreach (ManagementObject device in systemDevices)

{

if (device["ElementName"].ToString().ToLower() == deviceObjectElementName.ToLower())

{

systemDevice = device;

break;

}

}

return systemDevice;

}

public static bool JobCompleted(ManagementBaseObject outParams, ManagementScope scope)

{

bool jobCompleted = true;

//Retrieve msvc_StorageJob path. This is a full wmi path

string JobPath = (string)outParams["Job"];

ManagementObject Job = new ManagementObject(scope, new ManagementPath(JobPath), null);

//Try to get storage job information

Job.Get();

while ((UInt16)Job["JobState"] == JobState.Starting

|| (UInt16)Job["JobState"] == JobState.Running)

{

Console.WriteLine("In progress... {0}% completed.", Job["PercentComplete"]);

System.Threading.Thread.Sleep(1000);

Job.Get();

}

//Figure out if job failed

UInt16 jobState = (UInt16)Job["JobState"];

if (jobState != JobState.Completed)

{

UInt16 jobErrorCode = (UInt16)Job["ErrorCode"];

Console.WriteLine("Error Code:{0}", jobErrorCode);

Console.WriteLine("ErrorDescription: {0}", (string)Job["ErrorDescription"]);

jobCompleted = false;

}

return jobCompleted;

}

public static ManagementObject GetTargetComputer(string vmElementName, ManagementScope scope)

{

string query = string.Format("select * from Msvm_ComputerSystem Where ElementName = '{0}'", vmElementName);

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, new ObjectQuery(query));

ManagementObjectCollection computers = searcher.Get();

ManagementObject computer = null;

foreach (ManagementObject instance in computers)

{

computer = instance;

break;

}

return computer;

}

public static ManagementObject GetVirtualSystemSettingData(ManagementObject vm)

{

ManagementObject vmSetting = null;

ManagementObjectCollection vmSettings = vm.GetRelated

(

"Msvm_VirtualSystemSettingData",

"Msvm_SettingsDefineState",

null,

null,

"SettingData",

"ManagedElement",

false,

null

);

if (vmSettings.Count != 1)

{

throw new Exception(String.Format("{0} instance of Msvm_VirtualSystemSettingData was found", vmSettings.Count));

}

foreach (ManagementObject instance in vmSettings)

{

vmSetting = instance;

break;

}

return vmSetting;

}

enum ValueRole

{

Default = 0,

Minimum = 1,

Maximum = 2,

Increment = 3

}

enum ValueRange

{

Default = 0,

Minimum = 1,

Maximum = 2,

Increment = 3

}

//

// Get RASD definitions

//

public static ManagementObject GetResourceAllocationsettingDataDefault

(

ManagementScope scope,

UInt16 resourceType,

string resourceSubType,

string otherResourceType

)

{

ManagementObject RASD = null;

string query = String.Format("select * from Msvm_ResourcePool where ResourceType = '{0}' and ResourceSubType ='{1}' and OtherResourceType = '{2}'",

resourceType, resourceSubType, otherResourceType);

if (resourceType == ResourceType.Other)

{

query = String.Format("select * from Msvm_ResourcePool where ResourceType = '{0}' and ResourceSubType = null and OtherResourceType = {1}",

resourceType, otherResourceType);

}

else

{

query = String.Format("select * from Msvm_ResourcePool where ResourceType = '{0}' and ResourceSubType ='{1}' and OtherResourceType = null",

resourceType, resourceSubType);

}

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, new ObjectQuery(query));

ManagementObjectCollection poolResources = searcher.Get();

//Get pool resource allocation ability

if (poolResources.Count == 1)

{

foreach (ManagementObject poolResource in poolResources)

{

ManagementObjectCollection allocationCapabilities = poolResource.GetRelated("Msvm_AllocationCapabilities");

foreach (ManagementObject allocationCapability in allocationCapabilities)

{

ManagementObjectCollection settingDatas = allocationCapability.GetRelationships("Msvm_SettingsDefineCapabilities");

foreach (ManagementObject settingData in settingDatas)

{

if (Convert.ToInt16(settingData["ValueRole"]) == (UInt16)ValueRole.Default)

{

RASD = new ManagementObject(settingData["PartComponent"].ToString());

break;

}

}

}

}

}

return RASD;

}

public static ManagementObject GetResourceAllocationsettingData

(

ManagementObject vm,

UInt16 resourceType,

string resourceSubType,

string otherResourceType

)

{

//vm->vmsettings->RASD for IDE controller

ManagementObject RASD = null;

ManagementObjectCollection settingDatas = vm.GetRelated("Msvm_VirtualSystemsettingData");

foreach (ManagementObject settingData in settingDatas)

{

//retrieve the rasd

ManagementObjectCollection RASDs = settingData.GetRelated("Msvm_ResourceAllocationsettingData");

foreach (ManagementObject rasdInstance in RASDs)

{

if (Convert.ToUInt16(rasdInstance["ResourceType"]) == resourceType)

{

//found the matching type

if (resourceType == ResourceType.Other)

{

if (rasdInstance["OtherResourceType"].ToString() == otherResourceType)

{

RASD = rasdInstance;

break;

}

}

else

{

if (rasdInstance["ResourceSubType"].ToString() == resourceSubType)

{

RASD = rasdInstance;

break;

}

}

}

}

}

return RASD;

}

}