Delphi通过WMI设置Hyper-V虚拟机网络配置PUT操作无效求助
问题:Delphi通过WMI设置Hyper-V虚拟机静态IP,PUT操作返回成功但配置未生效
我正在用Delphi开发基于WMI的功能,目标是给Hyper-V虚拟机设置静态IP,但遇到异常:PUT操作返回成功,可虚拟机的网络配置根本没变化。已经用管理员身份连接本地Hyper-V主机,而且PowerShell方案能成功实现这个功能。
可正常运行的PowerShell代码
Invoke-Command -ComputerName $VMhost -ArgumentList $IPaddress, $Mask, $Gatew, $VMname -ScriptBlock { [string]$VMname = $args[3] $VMManServ = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemManagementService $vm = Get-WmiObject -Namespace 'root\virtualization\v2' -Class 'Msvm_ComputerSystem' | Where-Object { $_.ElementName -eq $VMname } $vmSettings = $vm.GetRelated('Msvm_VirtualSystemSettingData') | Where-Object { $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized' } $nwAdapters = $vmSettings.GetRelated('Msvm_SyntheticEthernetPortSettingData') $ipstuff = $nwAdapters.getrelated('Msvm_GuestNetworkAdapterConfiguration') $ipstuff.DHCPEnabled = $false $ipstuff.DNSServers = $args[2] $ipstuff.IPAddresses = $args[0] $ipstuff.Subnets = $args[1] $ipstuff.DefaultGateways = $args[2] $VMManServ.SetGuestNetworkAdapterConfiguration($VM, $ipstuff.GetText(1)) }
我的Delphi实现代码
procedure TForm2.SetNetworkAdapterConfiguration; var FSWbemLocator: ISWbemLocator; FWMIService: ISWbemServices; FWbemObjectSet: ISWbemObjectSet; NetworkAdapterConfig: ISWbemObject; ReturnValue: ISWbemObjectPath; begin try FSWbemLocator := CoSWbemLocator.Create; FWMIService := FSWbemLocator.ConnectServer('', 'root\virtualization\v2', '', '', '', '', 0, nil); FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM Msvm_GuestNetworkAdapterConfiguration WHERE InstanceID = "Microsoft:GuestNetwork\\CE89678D-73B0-4957-B7BF-73A42ABEDB52\\4FC2700C-A2F3-438A-80FB-078FDB33E67D"', 'WQL', 0, nil); if not Assigned(FWbemObjectSet) or (FWbemObjectSet.Count = 0) then begin ShowMessage('Config not found.'); Exit; end; NetworkAdapterConfig := FWbemObjectSet.ItemIndex(0) as ISWBemObject; Log1(FWbemObjectSet.ItemIndex(0).GetObjectText_(1)); NetworkAdapterConfig.Properties_.Item('DHCPEnabled', 0).Set_Value(OleVariant(False)); NetworkAdapterConfig.Properties_.Item('DNSServers', 0).Set_Value(VarArrayOf(['8.8.8.8'])); NetworkAdapterConfig.Properties_.Item('IPAddresses', 0).Set_Value(VarArrayOf(['192.168.0.199'])); NetworkAdapterConfig.Properties_.Item('Subnets', 0).Set_Value(VarArrayOf(['255.255.255.0'])); NetworkAdapterConfig.Properties_.Item('DefaultGateways', 0).Set_Value(VarArrayOf(['192.168.0.1'])); Log2(NetworkAdapterConfig.GetObjectText_(1)); ReturnValue := NetworkAdapterConfig.Put_(1, nil); ShowMessage('Success.'); Log3(ISWbemObjectPathVarDump(ReturnValue)); except on E: Exception do ShowMessage('Error: ' + E.Message); end; end;
当前现象
日志显示Msvm_GuestNetworkAdapterConfiguration对象已经被修改,PUT_方法返回了有效的ISWbemObjectPath,而且通过WMI-Activity追踪到PUTInstance操作已经在Hyper-V服务器上执行,但虚拟机的网络配置还是没更新。另外因为需求限制,不能用DHCP,必须给现有虚拟机动态指定静态IP和VLAN。
解决方案
问题出在你直接调用Msvm_GuestNetworkAdapterConfiguration的Put_方法,而Hyper-V的网络配置更新必须通过Msvm_VirtualSystemManagementService的SetGuestNetworkAdapterConfiguration方法来完成,这和你PowerShell里的逻辑一致,但Delphi代码里没这么做。
修改后的Delphi代码示例
procedure TForm2.SetNetworkAdapterConfiguration; var FSWbemLocator: ISWbemLocator; FWMIService: ISWbemServices; FWbemObjectSet: ISWbemObjectSet; NetworkAdapterConfig: ISWbemObject; VMSvc: ISWbemObject; VM: ISWbemObject; InParams: ISWbemObject; OutParams: ISWbemObject; ObjText: WideString; begin try FSWbemLocator := CoSWbemLocator.Create; FWMIService := FSWbemLocator.ConnectServer('', 'root\virtualization\v2', '', '', '', '', 0, nil); // 获取目标虚拟机的Msvm_ComputerSystem实例(替换成你的虚拟机名称) FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM Msvm_ComputerSystem WHERE ElementName = ''你的虚拟机名称''', 'WQL', 0, nil); if (FWbemObjectSet.Count = 0) then begin ShowMessage('虚拟机未找到'); Exit; end; VM := FWbemObjectSet.ItemIndex(0) as ISWBemObject; // 获取Msvm_VirtualSystemManagementService实例 FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM Msvm_VirtualSystemManagementService', 'WQL', 0, nil); if (FWbemObjectSet.Count = 0) then begin ShowMessage('无法获取虚拟机管理服务'); Exit; end; VMSvc := FWbemObjectSet.ItemIndex(0) as ISWBemObject; // 获取目标GuestNetworkAdapterConfiguration FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM Msvm_GuestNetworkAdapterConfiguration WHERE InstanceID = ''Microsoft:GuestNetwork\\CE89678D-73B0-4957-B7BF-73A42ABEDB52\\4FC2700C-A2F3-438A-80FB-078FDB33E67D''', 'WQL', 0, nil); if not Assigned(FWbemObjectSet) or (FWbemObjectSet.Count = 0) then begin ShowMessage('Config not found.'); Exit; end; NetworkAdapterConfig := FWbemObjectSet.ItemIndex(0) as ISWBemObject; // 修改配置属性 NetworkAdapterConfig.Properties_.Item('DHCPEnabled', 0).Set_Value(OleVariant(False)); NetworkAdapterConfig.Properties_.Item('DNSServers', 0).Set_Value(VarArrayOf(['8.8.8.8'])); NetworkAdapterConfig.Properties_.Item('IPAddresses', 0).Set_Value(VarArrayOf(['192.168.0.199'])); NetworkAdapterConfig.Properties_.Item('Subnets', 0).Set_Value(VarArrayOf(['255.255.255.0'])); NetworkAdapterConfig.Properties_.Item('DefaultGateways', 0).Set_Value(VarArrayOf(['192.168.0.1'])); // 获取对象的CIM文本 ObjText := NetworkAdapterConfig.GetObjectText_(1); // 获取SetGuestNetworkAdapterConfiguration的输入参数 InParams := VMSvc.Methods_.Item('SetGuestNetworkAdapterConfiguration', 0).InParameters.SpawnInstance_(); InParams.Properties_.Item('ComputerSystem', 0).Set_Value(VM.Path_); // 传入虚拟机路径 InParams.Properties_.Item('ConfigurationData', 0).Set_Value(ObjText); // 传入修改后的配置文本 // 调用方法 OutParams := VMSvc.ExecMethod_('SetGuestNetworkAdapterConfiguration', InParams, 0, nil); // 检查返回值(0表示成功) if (OutParams.Properties_.Item('ReturnValue', 0).Value = 0) then ShowMessage('配置更新成功') else ShowMessage('配置更新失败,返回值: ' + IntToStr(OutParams.Properties_.Item('ReturnValue', 0).Value)); except on E: Exception do ShowMessage('Error: ' + E.Message); end; end;
关键说明
- Hyper-V的
Msvm_GuestNetworkAdapterConfiguration是一个关联类,直接PUT不会触发实际的虚拟机配置更新,必须通过管理服务的专用方法提交 - 调用
SetGuestNetworkAdapterConfiguration时,需要传入虚拟机的Msvm_ComputerSystem路径和修改后的配置对象文本(对应PowerShell里的$ipstuff.GetText(1)) - 注意方法返回值的检查,不同的返回值对应不同的错误(比如32表示虚拟机未运行,需要虚拟机处于运行状态才能修改Guest网络配置)
内容的提问来源于stack exchange,提问作者Diego Carrilho
相关产品推荐
相关产品推荐

