I'm playing around with the Hyper-V WMI interface and am trying to create a snapshot of one of my local Hyper-V VMs, using C#. I'm trying to do this WITHOUT using System.Management. Instead, I'm using using Microsoft.Management.Infrastructure. Reason for this is that it is supported on .NET Core. Also, the System.Management.Infrastructure seems to be an intended replacement for System.Management.
I'm having trouble passing the correct parameters to the correct parameter to the "CreateSnapshot" method on the CIM_VirtualSystemSnapshotService class.
It is documented here: https://docs.microsoft.com/en-us/windows/desktop/hyperv_v2/cim-virtualsystemsnapshotservice-createsnapshot
The input parameters are listed:
uint32 CreateSnapshot( [in] CIM_ComputerSystem REF AffectedSystem, [in] string
SnapshotSettings, [in] uint16
SnapshotType, [in, out] CIM_VirtualSystemSettingData REF ResultingSnapshot, [out] CIM_ConcreteJob REF Job );
But, this does not specify which ones are mandatory, whether or not NULL values can be passed, etc.
The C# method I'm trying to use:
public static void CreateSnapshot()
{
const string hvNamespace = @"root\virtualization\v2";
var sessionOptions = new DComSessionOptions
{
Timeout = TimeSpan.FromSeconds(30)
};
var cimSession = CimSession.Create("localhost", sessionOptions);
var vmSnapshotService = new CimInstance(cimSession.GetClass(hvNamespace, "CIM_VirtualSystemSnapshotService"));
// CimInstance of CIM_ComputerSystem. QueryInstances returns Msvm_ComputerSystem
var vm = cimSession.QueryInstances(hvNamespace, "WQL", $"SELECT * FROM CIM_ComputerSystem WHERE ElementName = 'Android'").First();
var snapshotSettingDataClass = cimSession.GetClass(hvNamespace, "CIM_VirtualSystemSettingData");
var snapshotSettingData = new CimInstance(snapshotSettingDataClass);
var snapshotParameters = new CimMethodParametersCollection();
snapshotParameters.Add(CimMethodParameter.Create("AffectedSystem", vm, CimFlags.In));
snapshotParameters.Add(CimMethodParameter.Create("SnapshotSettings", "", CimFlags.In));
snapshotParameters.Add(CimMethodParameter.Create("SnapshotType", 2, CimFlags.In));
snapshotParameters.Add(CimMethodParameter.Create("ResultingSnapshot", snapshotSettingData, CimFlags.Out));
cimSession.InvokeMethod(namespaceName: hvNamespace, instance: vmSnapshotService, methodName: "CreateSnapshot", methodParameters: snapshotParameters);
//Microsoft.Management.Infrastructure.CimException: 'Invalid parameter '
Console.WriteLine($"Snapshot created!");
}
This gives the error "Invalid parameter". Hardly specific.
I've tried to rebuilt this in Powershell:
$session = New-CimSession
$hvNamespace = "root\virtualization\v2"
$snapshotservice = Get-CimClass -ClassName "CIM_VirtualSystemSnapshotService" -Namespace $hvNamespace
$vm = Get-CimInstance -Namespace $hvNamespace -Query "SELECT * FROM CIM_ComputerSystem WHERE ElementName = 'Android'" -QueryDialect WQL
Invoke-CimMethod -ClassName "Msvm_VirtualSystemSnapshotService" -MethodName "CreateSnapshot" -Namespace $hvNamespace -Arguments @{ "AffectedSystem" = $vm ; "SnapshotSettings" = "" ; "SnapshotType" = 2 }
This gives this error:
Invoke-CimMethod : Type mismatch for parameter "AffectedSystem" At line:1 char:1 + Invoke-CimMethod -ClassName "Msvm_VirtualSystemSnapshotService" -Meth ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidType: (root\virtualiza...SnapshotService :String) [Invoke-CimMethod], CimException + FullyQualifiedErrorId : HRESULT 0x80041005,Microsoft.Management.Infrast
ructure.CimCmdlets.InvokeCimMethodCommand
So, it looks like the method is expecting a CIM_ComputerSystem. However, I am passing it a Msvm_ComputerSystem. From C#, it is "just" another CIMInstance. So, I cannot cast from one class to another, as I could with "normal" classes.
Is there any way for me to "cast" the Msvm_ComputerSystem to a CIM_ComputerSystem? Or, am I chasing a large red herring?
OK, it took me a while before I found some time to work on this again.
I've finally solved it.
There are some things in the CIM / WMI framework which seem to work a bit different from what you'd expect.
The solution, in the end, is "easy". In a call to CimMethodParameter.Create, it is possible to give the CimType. Set this to the correct value, and the method will work. Any deviation will immediately result in a "Invalid parameter" or "Not found". For example, passing an int instead of a uint8 results in an error.
Also, it does not seem to be necessary to pre-define output variables. Output is returned by the CimMethodResult object returned from InvokeMethod.
I've included the full program below. Install Microsoft.Management.Infrastructure from Nuget before running.
using System;
using System.Linq;
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Options;
namespace TestApp
{
class Program
{
public static void CreateSnapshot()
{
const string hvNamespace = @"root\virtualization\v2";
var sessionOptions = new DComSessionOptions
{
Timeout = TimeSpan.FromSeconds(30)
};
var cimSession = CimSession.Create("localhost", sessionOptions);
// Get an instance of the VM to snapshot
var vm = cimSession.QueryInstances(hvNamespace, "WQL", $"SELECT * FROM CIM_ComputerSystem WHERE ElementName = 'MyTestVM'").First();
// Get the instance of Msvm_VirtualSystemSnapshotService. There is only one.
var vmSnapshotService = cimSession.EnumerateInstances(hvNamespace, "Msvm_VirtualSystemSnapshotService").First();
// Set the snapshot parameters by creating a Msvm_VirtualSystemSnapshotSettingData
var snapshotSettings = new CimInstance("Msvm_VirtualSystemSnapshotSettingData");
snapshotSettings.CimInstanceProperties.Add(CimProperty.Create("ConsistencyLevel", 1, CimType.UInt8, CimFlags.ReadOnly));
snapshotSettings.CimInstanceProperties.Add(CimProperty.Create("IgnoreNonSnapshottableDisks", true, CimFlags.ReadOnly));
// Put all of these things into a CimMethodParametersCollection.
// Note; no need to specify the "Out" parameters. They will be returned by the call to InvokeMethod.
var methodParameters = new CimMethodParametersCollection
{
CimMethodParameter.Create("AffectedSystem", vm, CimType.Reference, CimFlags.In),
CimMethodParameter.Create("SnapshotSettings", snapshotSettings.ToString(), CimType.String, CimFlags.In),
CimMethodParameter.Create("SnapshotType", 2, CimType.UInt16, CimFlags.In),
};
cimSession.InvokeMethod(hvNamespace, vmSnapshotService, "CreateSnapshot", methodParameters);
Console.WriteLine($"Snapshot created!");
}
static void Main(string[] args)
{
CreateSnapshot();
Console.ReadLine();
}
}
}
User contributions licensed under CC BY-SA 3.0