在 CIMInstance 中转换参数

Casting parameters inside CIMInstance

我正在尝试使用 Hyper-V WMI 界面,并尝试使用 C# 创建我的一个本地 Hyper-V VM 的快照。我试图在不使用 System.Management 的情况下执行此操作。相反,我使用 Microsoft.Management.Infrastructure。这样做的原因是它在 .NET Core 上受支持。此外,System.Management.Infrastructure 似乎是 System.Management.

的预期替代品

我无法将正确的参数传递给 CIM_VirtualSystemSnapshotService class.

上的 "CreateSnapshot" 方法的正确参数

它记录在这里: https://docs.microsoft.com/en-us/windows/desktop/hyperv_v2/cim-virtualsystemsnapshotservice-createsnapshot

输入参数列表:

uint32 CreateSnapshot( [in] CIM_ComputerSystem REF AffectedSystem, [in] string
SnapshotSettings, [in] uint16
SnapshotType, [in, out] CIM_VirtualSystemSettingData REF ResultingSnapshot, [out] CIM_ConcreteJob REF Job );

但是,这里并没有说明哪些是必须的,是否可以传递NULL值等等

我正在尝试使用的 C# 方法:

    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!");
    }

这给出了错误 "Invalid parameter"。很难具体。

我已经尝试在 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 }

这给出了这个错误:

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

所以,看起来该方法需要 CIM_ComputerSystem。但是,我将它传递给 Msvm_ComputerSystem。 在 C# 中,它是 "just" 另一个 CIMInstance。所以,我不能从一个 class 转换到另一个,就像我可以使用 "normal" classes.

有什么方法可以让我 "cast" 把 Msvm_ComputerSystem 变成 CIM_ComputerSystem 吗?或者,我是在追大红鲱鱼吗?

好吧,我花了一段时间才找到时间再次处理这个问题。

终于解决了

CIM/WMI 框架中的某些功能似乎与您预期的有点不同。

最终的解决方案是"easy"。在对 CimMethodParameter.Create 的调用中,可以提供 CimType。将其设置为正确的值,该方法将起作用。任何偏差都会立即导致 "Invalid parameter" 或 "Not found"。例如,传递 int 而不是 uint8 会导致错误。

另外,似乎没有必要预先定义输出变量。输出由从 InvokeMethod 返回的 CimMethodResult 对象返回。

我在下面包含了完整的程序。在 运行.

之前从 Nuget 安装 Microsoft.Management.Infrastructure
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();
        }
    }
}