Object not resolving while using WMI for HyperV - c#

I am using the official example from Microsoft docs to use WMI to start and shut down the virtual machine but Utility and ReturnCode objects aren't getting resolved. When I build the application I get
CS0103 The name 'Utility' does not exist in the current context
I am clueless
ManagementObject vm = Utility.GetTargetComputer(vmName, scope);
&
if ((UInt32)outParams["ReturnValue"] == ReturnCode.Started)
https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/requeststatechange-msvm-computersystem
Running everything on Server 2019 with Hyper-V running proper.
Here is the complete code:
using System;
using System.Management;
namespace HyperVSamples
{
public class RequestStateChangeClass
{
public static void RequestStateChange(string vmName, string action)
{
ManagementScope scope = new ManagementScope(#"\\.\root\virtualization\v2", null);
ManagementObject vm = Utility.GetTargetComputer(vmName, scope);
if (null == vm)
{
throw new ArgumentException(
string.Format(
"The virtual machine '{0}' could not be found.",
vmName));
}
ManagementBaseObject inParams = vm.GetMethodParameters("RequestStateChange");
const int Enabled = 2;
const int Disabled = 3;
if (action.ToLower() == "start")
{
inParams["RequestedState"] = Enabled;
}
else if (action.ToLower() == "stop")
{
inParams["RequestedState"] = Disabled;
}
else
{
throw new Exception("Wrong action is specified");
}
ManagementBaseObject outParams = vm.InvokeMethod(
"RequestStateChange",
inParams,
null);
if ((UInt32)outParams["ReturnValue"] == ReturnCode.Started)
{
if (Utility.JobCompleted(outParams, scope))
{
Console.WriteLine(
"{0} state was changed successfully.",
vmName);
}
else
{
Console.WriteLine("Failed to change virtual system state");
}
}
else if ((UInt32)outParams["ReturnValue"] == ReturnCode.Completed)
{
Console.WriteLine(
"{0} state was changed successfully.",
vmName);
}
else
{
Console.WriteLine(
"Change virtual system state failed with error {0}",
outParams["ReturnValue"]);
}
}
public static void Main(string[] args)
{
if (args != null && args.Length != 2)
{
Console.WriteLine("Usage: <application> vmName action");
Console.WriteLine("action: start|stop");
return;
}
RequestStateChange(args[0], args[1]);
}
}
}

You have to use a Query instead now:
ManagementScope scope = new ManagementScope(#"root\virtualization\v2");
ObjectQuery query = new ObjectQuery(#"SELECT * FROM Msvm_ComputerSystem WHERE ElementName = '" + vmName + "'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection collection = searcher.Get();
ManagementObject vm = null;
foreach (ManagementObject obj in collection)
{
vm = obj;
break;
}
For the ReturnCode use your own constant instead. You can look them up here: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/virtual/requeststatechange-msvm-computersystem
Something like this:
if ((UInt32)outParams["ReturnValue"] == 0)
{
...
}
0 == all good. Use UInt32 not UInt16!
Remember to run your app as administrator!

Related

Kill All Spawned Chromes

I have a project that has the following methods.
using System.Management;
public void KillAllSpawnedChromes() {
var chromeProcs = GetMyChildChromeProcesses();
_logger.Info("Found {0} chrome processess still running. Killing them", chromeProcs.Count());
foreach (var chromeProc in chromeProcs) {
chromeProc.Kill();
}
}
private static IEnumerable<Process> GetMyChildChromeProcesses() {
var myCurrentProcess = Process.GetCurrentProcess();
var children = new List<Process>();
var mos = new ManagementObjectSearcher(
$"Select * From Win32_Process Where ParentProcessID={myCurrentProcess.Id}");
foreach (var o in mos.Get()) {
var mo = (ManagementObject)o;
children.Add(Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])));
}
return children.Where(x => x.ProcessName.Contains("chrome"));
}
}
I would like to delete this dependency from System.Managment. Is there any other way to kill Chrome processes?
You could use the following Extension Method:
// from https://code-examples.net/en/q/60640
public static class ProcessExtensions
{
private static string FindIndexedProcessName(int pid)
{
try
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
catch(Exception)
{
return "";
}
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
try
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
catch(Exception)
{
return null;
}
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
The kill loop is now rather simple:
Process[] processes = Process.GetProcessesByName("chrome");
int pid = Process.GetCurrentProcess().Id;
foreach (Process process in processes)
{
var parent = process.Parent();
if ((parent != null) && (pid == parent.Id))
{
process.Kill();
}
}
Note that a single instance of the Chrome browser usually spawns several processes. It might be necessary to kill them or properly shut them down in a specific order. Unforeseen damage of the Chrome data files might happen, unless you are careful.

Determine from which location an application was being started

Is there any possibility to determine how a c# application was being started?
In my case I want to check if this application (wpf) is being started by a shortcut located in a specific folder.
So, there are two ways to open my application
using direct shortcut
starting another application which is like an update manager to keep my application up to date. After checking, it starts my application with Process.Start()
And I want to ensure that the application is only able to be started with the update manager.
A trick you could use is to check the parent's PID, and then get some of the parent's process information.
If the parent's process name is something like "explorer.exe" then the application was started from the shortcut or directly by double-clicking it on explorer.
Otherwise, it was started from another application: it could be your updater application, it could also be another application with the same name as your updater application...
This means you have to re-think how deep you want to go for such a solution, and how deep do you want security control. You could pass arguments from your updater to your main application, or implement some inter-process communication with token exchanges... it is impossible to make a 100% secure system.
As someone commented above, this seems like a XY problem... or maybe not. Maybe it is just a security concern. It's recommended to revise what exactly are you aiming for this software.
In case you need sample code for retrieving process information in .NET (by using System.Management), then just give a try to the code listed below. All you have to do is to place it in a console application project named 'Updater', and correctly set the path to your main application in the code.
If you play a little bit with this example by starting and closing YourApplication.exe in different situations, then you should be able to see an output like this:
Parent process 'Updater.exe' [PID=5472]
Parent process 'explorer.exe' [PID=12052]
The code below was tested on VS2017 .Net 4.6.1
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
class Program
{
static void Main(string[] args)
{
Process.Start(new ProcessStartInfo()
{
FileName = "YourApplication.exe" // path to your application
});
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
Process process = Process.GetProcessesByName("YourApplication").FirstOrDefault(); // your application's process name
if (process == null)
{
Console.WriteLine($"Process is not running...");
continue;
}
ProcessManager pm = ProcessManager.FromLocalMachine();
var processProperties = pm.GetProcessProperties(process.Id);
int parentProcessId = Convert.ToInt32(processProperties[EProcessProperty.ParentProcessId]);
try
{
var parentProcessProperties = pm.GetProcessProperties(parentProcessId);
string parentProcessName = parentProcessProperties[EProcessProperty.Name].ToString();
Console.WriteLine($"Parent process '{parentProcessName ?? "Unknown"}' [PID={parentProcessId}]");
Console.WriteLine("---------------------------------");
}
catch { Console.WriteLine("Parent process information not found."); }
}
}
}
public class ProcessConnection
{
internal ManagementScope ManagementScope { get; }
internal ProcessConnection(string machineName, string user = null, string password = null, string domain = null)
{
ManagementScope = new ManagementScope
{
Path = new ManagementPath(#"\\" + machineName + #"\root\CIMV2"),
Options = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
Authentication = AuthenticationLevel.Default,
EnablePrivileges = true,
Username = user == null ? null : (string.IsNullOrWhiteSpace(domain) ? user : $"{domain}\\{user}"),
Password = user == null ? null : password,
},
};
ManagementScope.Connect();
}
}
public class ProcessManager
{
public static ProcessManager FromLocalMachine() => new ProcessManager()
{
Machine = Environment.MachineName,
};
public static ProcessManager FromRemoteMachine(string machine, string user = null, string password = null, string domain = null) => new ProcessManager()
{
Machine = machine,
User = user,
Password = password,
Domain = domain,
};
private ProcessManager() { }
public string Machine { get; private set; }
public string User { get; private set; }
public string Password { get; private set; }
public string Domain { get; private set; }
private ProcessConnection Connection { get; set; }
private ManagementScope ManagementScope => Connection == null ? (Connection = new ProcessConnection(Machine, User, Password, Domain)).ManagementScope : Connection.ManagementScope;
public EProcessStartStatus StartProcess(string processPath)
{
ManagementClass mc = new ManagementClass($"\\\\{Machine}\\root\\CIMV2", "Win32_Process", null);
ManagementBaseObject process = mc.GetMethodParameters("Create");
process["CommandLine"] = processPath;
ManagementBaseObject createCode = mc.InvokeMethod("Create", process, null);
string createCodeStr = createCode["ReturnValue"].ToString();
return (EProcessStartStatus)Convert.ToInt32(createCodeStr);
}
public bool KillProcess(string processName)
{
try
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get()) mo.InvokeMethod("Terminate", null);
return true;
}
catch { return false; }
}
public bool KillProcess(int processId)
{
try
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE ProcessId = '{processId}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get()) mo.InvokeMethod("Terminate", null);
return true;
}
catch { return false; }
}
public void SetProcessPriority(string processName, EProcessPriority priority)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject managementObject in managementObjectSearcher.Get())
{
ManagementBaseObject methodParams = managementObject.GetMethodParameters("SetPriority");
methodParams["Priority"] = priority;
managementObject.InvokeMethod("SetPriority", methodParams, null);
}
}
public string GetProcessOwner(string processName)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get())
{
ManagementBaseObject methodParams = mo.GetMethodParameters("GetOwner");
ManagementBaseObject owner = mo.InvokeMethod("GetOwner", null, null);
return owner["User"].ToString();
}
return null;
}
public string GetProcessOwnerSID(string processName)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get())
{
ManagementBaseObject methodParams = mo.GetMethodParameters("GetOwnerSid");
ManagementBaseObject OwnerSid = mo.InvokeMethod("GetOwnerSid", null, null);
return OwnerSid["Sid"].ToString();
}
return null;
}
public IList<int> GetRunningProcesses()
{
IList<int> processes = new List<int>();
SelectQuery query = new SelectQuery("SELECT * FROM Win32_Process");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get()) processes.Add(int.Parse(mo["ProcessId"].ToString()));
return processes;
}
public IDictionary<EProcessProperty, object> GetProcessProperties(int processId)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE ProcessId = '{processId}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
Dictionary<EProcessProperty, object> properties = new Dictionary<EProcessProperty, object>();
foreach (ManagementObject mo in searcher.Get())
{
foreach (PropertyData pd in mo.Properties)
{
if (Enum.TryParse(pd.Name, out EProcessProperty e)) properties[e] = pd.Value;
else Console.WriteLine(pd.Name + " is not mapped in the properties enumeration.");
}
}
return properties;
}
public IDictionary<EProcessProperty, object> GetProcessProperties(string processName)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
Dictionary<EProcessProperty, object> properties = new Dictionary<EProcessProperty, object>();
foreach (ManagementObject mo in searcher.Get())
{
foreach (PropertyData pd in mo.Properties)
{
if (Enum.TryParse(pd.Name, out EProcessProperty e)) properties[e] = pd.Value;
else Console.WriteLine(pd.Name + " is not mapped in the properties enumeration.");
}
}
return properties;
}
public IList<int> GetProcessessFromExecutablePath(string executablePath)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE ExecutablePath = '{executablePath.Replace("\\", "\\\\")}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
return searcher.Get().Cast<ManagementObject>().Select(mo => Convert.ToInt32(mo["ProcessId"])).ToList();
}
}
public enum EProcessPriority : uint
{
IDLE = 0x40,
BELOW_NORMAL = 0x4000,
NORMAL = 0x20,
ABOVE_NORMAL = 0x8000,
HIGH_PRIORITY = 0x80,
REALTIME = 0x100
}
public enum EProcessStartStatus
{
Success = 0,
AccessDenied = 2,
NoPermissions = 3,
Unknown = 8,
FileNotFound = 9,
Invalid = 21,
}
public enum EProcessProperty
{
Caption,
CommandLine,
CreationClassName,
CreationDate,
CSCreationClassName,
CSName,
Description,
ExecutablePath,
ExecutionState,
Handle,
HandleCount,
InstallDate,
KernelModeTime,
MaximumWorkingSetSize,
MinimumWorkingSetSize,
Name,
OSCreationClassName,
OSName,
OtherOperationCount,
OtherTransferCount,
PageFaults,
PageFileUsage,
ParentProcessId,
PeakPageFileUsage,
PeakVirtualSize,
PeakWorkingSetSize,
Priority,
PrivatePageCount,
ProcessId,
QuotaNonPagedPoolUsage,
QuotaPagedPoolUsage,
QuotaPeakNonPagedPoolUsage,
QuotaPeakPagedPoolUsage,
ReadOperationCount,
ReadTransferCount,
SessionId,
Status,
TerminationDate,
ThreadCount,
UserModeTime,
VirtualSize,
WindowsVersion,
WorkingSetSize,
WriteOperationCount,
WriteTransferCount,
}
If there are only 2 ways of starting your app, the second method should pass a parameter (a GUID?) to Process.Start() - generated by your updater app.
Maybe devise some kind of algorithm that allows the app to start only with the token.
From what I know this is impossible in the way you would like it to be but there's one trick which you can use. Firstly change your WPF application's entry method to get the command line arguments, and ( for example ) use -u argument to distinct from where the application was started. Then after -u you can pass a HWND or a process ID that matches your updater. Of course you have to then check if that application is running and if it's your updater.
example :
// updated process start
ProcessStartInfo psi = new ProcessStartInfo("your/WPF/application.exe");
psi.Arguments = "-u " + Process.GetCurrentProcess().Id;
// fill up rest of the properties you need
Process.Start(psi);
// wpf application's entry point
void Main(string[] args)
{
string updaterProcessIdstr = string.Empty;
for (int i = 0; i < args.Length; i++)
{
if(args[i] == "-u")
{
updaterProcessIdstr = args[i + 1];
i++;
}
}
int pid = int.Parse(updaterProcessIdstr);
Process updaterProcess = Process.GetProcessById(pid);
// do some validation here
// send something to stdin and read from stdout
// to determine if it was started from that updater.
}

C#: Arduino not recognized after Windows 10 upgrade

Please check the code that I am using:
public static string AutoDetectArduinoPort()
{
ConnectionOptions options = PrepareOptions();
ManagementScope scope = PrepareScope(Environment.MachineName, options, #"\root\CIMV2");
// Prepare the query and searcher objects.
ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");
ManagementObjectSearcher portSearcher = new ManagementObjectSearcher(scope, objectQuery);
using (portSearcher)
{
string portName = "";
foreach (ManagementObject currentObject in portSearcher.Get())
{
if (currentObject != null)
{
object currentObjectCaption = currentObject["Caption"];
if (currentObjectCaption != null)
{
portName = currentObjectCaption.ToString();
if (portName.Contains("(COM"))
{
if (portName.Contains("Arduino")) // e.g. "Arduino Mega 2560 (COM3)"
{
portName = portName.Substring(portName.LastIndexOf("(COM")).Replace("(", string.Empty).Replace(")", string.Empty);
return portName;
}
}
}
}
}
}
return null;
}
And i call it like this:
string portName = AutoDetectArduinoPort();
MessageBox.Show(portName); // shows "COM3"
if (portName != null)
{
serialPort.PortName = portName;
serialPort.Open();
......
Runtime exception (System.IO.IOException) is thrown at this last line saying that "The port 'COM3' does not exist." while it is showing in Device Manager and all.
What am i doing wrong? Please help me fix this prob. Irony is that it was working all fine before windows 10 upgrade :(

How to Change DNS with C# on Windows 10

I'm trying to change the DNS on Windows 10 through VB.NET.
I have code that works on Windows 7, however it does not work on Windows 10.
Here is my code for Windows 7 that changes the DNS:
ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
if ((bool)mo["IPEnabled"])
{
ManagementBaseObject objdns = mo.GetMethodParameters("SetDNSServerSearchOrder");
if (objdns != null)
{
string[] s = { "192.168.XX.X", "XXX.XX.X.XX" };
objdns["DNSServerSearchOrder"] = s;
mo.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
My question is, how do I get this to work on Windows 10 OS?
First you need to get the NetworkInterface you want to set/unset DNS
I've tested this code on the latest version of Windows 10 and it works like a charm!
Here is the code to find the active Ethernet or Wifi network (Not 100% accurate but useful in most cases)
public static NetworkInterface GetActiveEthernetOrWifiNetworkInterface()
{
var Nic = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault(
a => a.OperationalStatus == OperationalStatus.Up &&
(a.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || a.NetworkInterfaceType == NetworkInterfaceType.Ethernet) &&
a.GetIPProperties().GatewayAddresses.Any(g => g.Address.AddressFamily.ToString() == "InterNetwork"));
return Nic;
}
SetDNS
public static void SetDNS(string DnsString)
{
string[] Dns = { DnsString };
var CurrentInterface = GetActiveEthernetOrWifiNetworkInterface();
if (CurrentInterface == null) return;
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if ((bool)objMO["IPEnabled"])
{
if (objMO["Description"].ToString().Equals(CurrentInterface.Description))
{
ManagementBaseObject objdns = objMO.GetMethodParameters("SetDNSServerSearchOrder");
if (objdns != null)
{
objdns["DNSServerSearchOrder"] = Dns;
objMO.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
}
}
}
}
}
UnsetDNS
public static void UnsetDNS()
{
var CurrentInterface = GetActiveEthernetOrWifiNetworkInterface();
if (CurrentInterface == null) return;
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if ((bool)objMO["IPEnabled"])
{
if (objMO["Description"].ToString().Equals(CurrentInterface.Description))
{
ManagementBaseObject objdns = objMO.GetMethodParameters("SetDNSServerSearchOrder");
if (objdns != null)
{
objdns["DNSServerSearchOrder"] = null;
objMO.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
}
}
}
}
}
Usage
SetDNS("127.0.0.1");
Combining multiple solutions I found that the following code is working great for Windows 10 and 8.1 (others not tested, but should work as well):
public static void setDNS(string NIC, string DNS)
{
ConnectionOptions options = PrepareOptions();
ManagementScope scope = PrepareScope(Environment.MachineName, options, #"\root\CIMV2");
ManagementPath managementPath = new ManagementPath("Win32_NetworkAdapterConfiguration");
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementClass mc = new ManagementClass(scope, managementPath, objectGetOptions);
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
if ((bool)mo["IPEnabled"])
{
if (mo["Caption"].ToString().Contains(NIC))
{
try
{
ManagementBaseObject newDNS = mo.GetMethodParameters("SetDNSServerSearchOrder");
newDNS["DNSServerSearchOrder"] = DNS.Split(',');
ManagementBaseObject setDNS = mo.InvokeMethod("SetDNSServerSearchOrder", newDNS, null);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Console.ReadKey();
throw;
}
}
}
}
}
The application needs to run with elevated permissions (in my case I'm starting an elevated process running an .exe):
private void callSwapDNS(string NIC, string DNS)
{
const int ERROR_CANCELLED = 1223; //The operation was canceled by the user.
ProcessStartInfo info = new ProcessStartInfo(#"swap.exe");
string wrapped = string.Format(#"""{0}"" ""{1}""", NIC, DNS);
info.Arguments = wrapped;
info.UseShellExecute = true;
info.Verb = "runas";
info.WindowStyle = ProcessWindowStyle.Hidden;
try
{
Process.Start(info);
Thread.Sleep(500);
}
catch (Win32Exception ex)
{
if (ex.NativeErrorCode == ERROR_CANCELLED)
MessageBox.Show("Why you no select Yes?");
else
throw;
}
}
Using mo["Caption"].ToString().Contains(NIC) doesn't work for Windows 10 as the WMI query returns the NIC-Name leading with [000000]
[000000] Intel(R) 82574L Gigabit Network Connection
on my Windows 10 machine.
Credit to the following answers: [WMI not working after upgrading to Windows 10
WMI not working after upgrading to Windows 10
How can you change Network settings (IP Address, DNS, WINS, Host Name) with code in C#
and the answers to this question.
With Windows 10 you may need authentication first. Pass a ConnectionOptions instance to a ManagementScope constructor, defining your Authentication and Impersonation properties.
Try this:
// Method to prepare the WMI query connection options.
public static ConnectionOptions PrepareOptions ( )
{
ConnectionOptions options = new ConnectionOptions ( );
options . Impersonation = ImpersonationLevel . Impersonate;
options . Authentication = AuthenticationLevel . Default;
options . EnablePrivileges = true;
return options;
}
// Method to prepare WMI query management scope.
public static ManagementScope PrepareScope ( string machineName , ConnectionOptions options , string path )
{
ManagementScope scope = new ManagementScope ( );
scope . Path = new ManagementPath ( #"\\" + machineName + path );
scope . Options = options;
scope . Connect ( );
return scope;
}
// Set DNS.
ConnectionOptions options = PrepareOptions ( );
ManagementScope scope = PrepareScope ( Environment . MachineName , options , #"\root\CIMV2" );
ManagementClass mc = new ManagementClass(scope, "Win32_NetworkAdapterConfiguration");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc)
{
if ((bool)mo["IPEnabled"])
{
ManagementBaseObject objdns = mo.GetMethodParameters("SetDNSServerSearchOrder");
if (objdns != null)
{
string[] s = { "192.168.XX.X", "XXX.XX.X.XX" };
objdns["DNSServerSearchOrder"] = s;
mo.InvokeMethod("SetDNSServerSearchOrder", objdns, null);
Based on this answer:
WMI not working after upgrading to Windows 10
This is the code I use to do this and it works:
/// <summary>
/// Set's the DNS Server of the local machine
/// </summary>
/// <param name="NIC">NIC address</param>
/// <param name="DNS">DNS server address</param>
/// <remarks>Requires a reference to the System.Management namespace</remarks>
public void setDNS(string NIC, string DNS)
{
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if ((bool)objMO["IPEnabled"])
{
// if you are using the System.Net.NetworkInformation.NetworkInterface you'll need to change this line to if (objMO["Caption"].ToString().Contains(NIC)) and pass in the Description property instead of the name
if (objMO["Caption"].Equals(NIC))
{
try
{
ManagementBaseObject newDNS =
objMO.GetMethodParameters("SetDNSServerSearchOrder");
newDNS["DNSServerSearchOrder"] = DNS.Split(',');
ManagementBaseObject setDNS =
objMO.InvokeMethod("SetDNSServerSearchOrder", newDNS, null);
}
catch (Exception)
{
throw;
}
}
}
}
}
Hope it helps...

Create a new Hyper-V VM (using WMI) with specific hardware

I'm wanting to create a new Hyper-V VM with a determined amount of RAM, network card, number of processor cores, and attach a VHD file to the IDE controller.
The problem is that the Msvm_ResourceAllocationSettingData is not very easy to work with. The code I'm using doesn't work (this is code to attach a VHD to an existing VM, however I would also like to use it when creating a new VHD too).
public void AttachVhd(IdeChannel ideChannel, String vhdPath) {
// Get VirtualSystemSettingData
ManagementObject vssd = this.GetVirtualSystemSettingData();
// Get the IDE Controller
ManagementObject ideController = this.GetResourceAllocationSettingData(ResourceType.IdeController, ResourceSubType.IdeController);
// Create synthetic disk:
ManagementObject syntheticDiskRasd = this.GetResourceAllocationSettingData(ResourceType.Disk, ResourceSubType.DiskSynthetic);
syntheticDiskRasd["Parent"] = ideController.Path;
syntheticDiskRasd["Address"] = ideChannel == IdeChannel.Primary ? "0" : "1";
this.AddVirtualSystemResources(syntheticDiskRasd);
// Attach it
ManagementObject vhdRasd = this.GetResourceAllocationSettingData(ResourceType.StorageExtent, ResourceSubType.Vhd); ;
vhdRasd["Parent"] = syntheticDiskRasd.Path;
vhdRasd["Connection"] = new String[] { vhdPath };
this.AddVirtualSystemResources( vhdRasd );
// Cleanup
vhdRasd.Dispose();
syntheticDiskRasd.Dispose();
ideController.Dispose();
vssd.Dispose();
}
private ManagementObject GetResourceAllocationSettingData(ResourceType resourceType, ResourceSubType resourceSubType)
{
String desiredSubType = ResourceSubTypeStrings.GetString(resourceSubType); // Scout.Common.Extensions.GetDescription( resourceType );
using(ManagementObjectCollection settingsDatas = _vm.GetRelated("Msvm_VirtualSystemSettingData"))
foreach(ManagementObject settingData in settingsDatas)
{
using(ManagementObjectCollection rasds = settingData.GetRelated("Msvm_ResourceAllocationSettingData"))
foreach(ManagementObject rasd in rasds)
{
ResourceType rasdResourceType = (ResourceType)(UInt16)rasd["ResourceType"];
String rasdResourceSubType = (String)rasd["ResourceSubType"];
String rasdOtherType = (String)rasd["OtherResourceType"];
if( rasdResourceType == resourceType && rasdResourceSubType == desiredSubType )
{
return rasd;
}
}
}
return null;
}
private void AddVirtualSystemResources(ManagementObject rasd)
{
using (ManagementObject vmService = HyperV.GetManagementService())
{
ManagementBaseObject inParams = vmService.GetMethodParameters("AddVirtualSystemResources");
inParams["TargetSystem"] = _vm;
inParams["ResourceSettingsData"] = rasd.GetText(TextFormat.CimDtd20);
ManagementBaseObject outParams = vmService.InvokeMethod("AddVirtualSystemResources", inParams, options: null);
String[] addedResources = (String[])outParams["NewResources"];
OperationReturnCode returnValue = (OperationReturnCode)(UInt32)outParams["ReturnValue"];
if (returnValue == OperationReturnCode.JobStarted)
{
String jobPath = (String)outParams["Job"];
HyperV.MonitorJob(jobPath);
}
else if (returnValue == OperationReturnCode.Completed)
{
}
else
{
throw new ApplicationException( returnValue.ToString() );
}
}
}
Rather than find your problem, can I point you to an example that works?
See WmiCalls.DeployVirtualMachine in my Apache CloudStack Hyper-V plugin
Post a comment if you need additional detail, and I will update the answer.

Categories