Invocation of Win32_Service UserControlService not working - c#

I'm using WMI to install/start/stop etc. services on a remote machine. This is working nicely, only the invocation of the UserControlService seems to be a problem.
I know that it would also be possible to impersonate a user and then use the ServiceController class, but as I've already got all other methods I would rather keep the WMI code and get my method to send service control requests working.
Following code:
public static string SendServiceControlRequest(string remoteHost, string serviceName, string username,
string password, int request)
{
ConnectionOptions theConnection = new ConnectionOptions();
theConnection.Username = username;
theConnection.Password = password;
ManagementScope theScope = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", remoteHost), theConnection);
using (ManagementObject theClass = new ManagementObject(theScope, new ManagementPath("Win32_Service"),
new ObjectGetOptions())) // causes an ArgumentOutOfRangeException (Parametername: path)
{
using (ManagementBaseObject inParams = theClass.GetMethodParameters("UserControlService"))
{
inParams["ControlCode"] = (Byte)request;
ManagementBaseObject outParams = theClass.InvokeMethod("UserControlService", inParams, null);
return outParams["ReturnValue"].ToString();
}
}
}
Throws a System.Management.ManagementException complaining about invalid parameters (with request of 150, which should work). The exception is thrown on theClass.InvokeMethod I'm not sure why this happen, I'm getting the description of the method from:
http://msdn.microsoft.com/en-us/library/aa393952(v=vs.85).aspx
Edit: Working version with the corrections by Hans Passant:
public static bool SendServiceControlRequest(string remoteHost, string serviceName, string username,
string password, int request)
{
ConnectionOptions theConnection = new ConnectionOptions();
theConnection.Username = username;
theConnection.Password = password;
ManagementScope theScope = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", remoteHost),
theConnection);
string servicePath = string.Format("Win32_Service.Name='{0}'", serviceName);
ManagementPath path = new ManagementPath(servicePath);
using (ManagementObject theClass = new ManagementObject(theScope, path,
new ObjectGetOptions()))
{
using (ManagementBaseObject inParams = theClass.GetMethodParameters("UserControlService"))
{
inParams["ControlCode"] = (Byte)request;
ManagementBaseObject outParams = theClass.InvokeMethod("UserControlService", inParams, null);
return outParams["ReturnValue"].ToString() == "0";
}
}
}

Yes, there's a bug in your code. You correctly added a serviceName argument to your method but you forgot to actually use it. That's important, you really do have to be specific about what particular service you send this control code to.
Make it look like this instead:
var path = string.Format("Win32_Service.Name='{0}'", serviceName);
var thePath = new ManagementPath(path);
var theClass = new ManagementObject(theScope, thePath, null);
// etc...

Instead of
inParams["ControlCode"] = (Byte)request;
try
inParams.SetPropertyValue("ControlCode", (Byte)request);
EDIT
Untested, but this seems to be what you might be looking for:
Instead of
using (ManagementBaseObject inParams = theClass.GetMethodParameters("UserControlService"))
{
inParams["ControlCode"] = (Byte)request;
ManagementBaseObject outParams = theClass.InvokeMethod("UserControlService", inParams, null);
return outParams["ReturnValue"].ToString();
}
Try utilizing the ManagementClass instances (from http://social.msdn.microsoft.com/Forums/vstudio/en-US/1dbe4995-ce73-4f01-8d9a-6cf1650bce8a/wmi-c-managementclassinvokemethod-failure):
foreach (var instance in theClass.GetInstances())
{
using (ManagementBaseObject inParams = instance.GetMethodParameters("UserControlService"))
{
inParams["ControlCode"] = (Byte)request;
ManagementBaseObject outParams = instance.InvokeMethod("UserControlService", inParams, null);
return outParams["ReturnValue"].ToString();
}
}

Related

Create domain in windows Dns using C # [duplicate]

I need some sample code to create/delete zone and A record in microsoft DNS server by C#
You have to use WMI to invoke the DNSProvider.
This to add a record:
public void AddARecord(string hostName, string zone, string iPAddress, string dnsServerName)
{
ManagementScope scope =
new ManagementScope(#"\\" + dnsServerName + "\\root\\MicrosoftDNS");
scope.Connect();
ManagementClass cmiClass =
new ManagementClass(scope,
new ManagementPath("MicrosoftDNS_AType"),
null);
ManagementBaseObject inParams =
cmiClass.GetMethodParameters("CreateInstanceFromPropertyData");
inParams["DnsServerName"] = this.ServerName;
inParams["ContainerName"] = zone;
inParams["OwnerName"] = hostName + "." + zone;
inParams["IPAddress"] = iPAddress;
cmiClass.InvokeMethod("CreateInstanceFromPropertyData", inParams, null);
}
You can reference the WMI reference and extend this as you need using the methods and classes
http://msdn.microsoft.com/en-us/library/ms682123(v=vs.85).aspx
Microsoft exposes it as a POX service, so you could just push XML over the wire to it, using the System.Net stuff & your user credentials.
http://technet.microsoft.com/en-us/library/dd278634.aspx
I agreed with Taylor but in my case i have got 2 different error with above code
1- Generic Error
2- Not Found error
Below code has solved this problems
private ManagementPath UpdateARecord(string strDNSZone, string strHostName, string strIPAddress)
{
ManagementScope mgmtScope = new ManagementScope(#"\\.\Root\MicrosoftDNS");
ManagementClass mgmtClass = null;
ManagementBaseObject mgmtParams = null;
ManagementObjectSearcher mgmtSearch = null;
ManagementObjectCollection mgmtDNSRecords = null;
string strQuery;
strQuery = string.Format("SELECT * FROM MicrosoftDNS_AType WHERE OwnerName = '{0}.{1}'", strHostName, strDNSZone);
mgmtScope.Connect();
mgmtSearch = new ManagementObjectSearcher(mgmtScope, new ObjectQuery(strQuery));
mgmtDNSRecords = mgmtSearch.Get();
//// Multiple A records with the same record name, but different IPv4 addresses, skip.
//if (mgmtDNSRecords.Count > 1)
//{
// // Take appropriate action here.
//}
//// Existing A record found, update record.
//else
if (mgmtDNSRecords.Count == 1)
{
ManagementObject mo = new ManagementObject();
foreach (ManagementObject mgmtDNSRecord in mgmtDNSRecords)
{
if (mgmtDNSRecord["RecordData"].ToString() != strIPAddress)
{
mgmtParams = mgmtDNSRecord.GetMethodParameters("Modify");
mgmtParams["IPAddress"] = strIPAddress;
mgmtDNSRecord.InvokeMethod("Modify", mgmtParams, null);
}
mo = mgmtDNSRecord;
break;
}
return new ManagementPath(mo["RR"].ToString());
}
// A record does not exist, create new record.
else
{
mgmtClass = new ManagementClass(mgmtScope, new ManagementPath("MicrosoftDNS_AType"), null);
mgmtParams = mgmtClass.GetMethodParameters("CreateInstanceFromPropertyData");
mgmtParams["DnsServerName"] = Environment.MachineName;
mgmtParams["ContainerName"] = strDNSZone;
mgmtParams["OwnerName"] = strDNSZone;// string.Format("{0}.{1}", strHostName.ToLower(), strDNSZone);
mgmtParams["IPAddress"] = strIPAddress;
var outParams = mgmtClass.InvokeMethod("CreateInstanceFromPropertyData", mgmtParams, null);
if ((outParams.Properties["RR"] != null))
{
return new ManagementPath(outParams["RR"].ToString());
}
}
return null;
}

Cannot connect Hyper-V VM Network Adapter to a Hyper-V Switch via WMI in C#

I am having quite some difficulty connecting a specific, existing network adapter to an existing switch. I can create a new network adapter and connect it to my VM through several examples posted online but cannot make that extra step. The following function finds my network adapter and executes without error, but does not otherwise make the connection. Any assistance is greatly appreciated!
**EDIT: Solved, see code below.**
Solved:
public static void ConnectInterfaceToSwitch(string VmName, string networkInterfaceName, string switchName)
{
ManagementScope scope = new ManagementScope(#"root\virtualization\v2");
ManagementObject mgtSvc = WmiUtilities.GetVirtualMachineManagementService(scope);
ManagementObject ethernetSwitch = NetworkingUtilities.FindEthernetSwitch(switchName, scope);
ManagementObject virtualMachine = WmiUtilities.GetVirtualMachine(VmName, scope);
ManagementObject virtualMachineSettings = WmiUtilities.GetVirtualMachineSettings(virtualMachine);
ManagementObjectCollection portsSettings = virtualMachineSettings.GetRelated("Msvm_SyntheticEthernetPortSettingData", "Msvm_VirtualSystemSettingDataComponent", null, null, null, null, false, null);
{
foreach (ManagementObject portSettings in portsSettings)
{
if (portSettings["ElementName"].Equals(networkInterfaceName))
{
Console.WriteLine("Adapter found: " + networkInterfaceName);
ManagementObjectCollection connections = portSettings.GetRelated("Msvm_EthernetPortAllocationSettingData");
foreach (ManagementObject connection in connections)
{
connection["HostResource"] = new string[] { ethernetSwitch.Path.Path };
connection["EnabledState"] = 2; // 2 means "Enabled"
ManagementBaseObject inParams = mgtSvc.GetMethodParameters("ModifyResourceSettings");
inParams["ResourceSettings"] = new string[] { connection.GetText(TextFormat.WmiDtd20) };
ManagementBaseObject outParams = mgtSvc.InvokeMethod("ModifyResourceSettings", inParams, null);
WmiUtilities.ValidateOutput(outParams, scope);
Console.WriteLine(string.Format(CultureInfo.CurrentCulture, "Connected VM '{0}' to switch '{1}'.", VmName, switchName));
}
}
}
}
}

how to get logged on users with their status on remote machine

I'm looking for a way to get the users that are logged in on a remote machine. I would love to know if they are logged on localy or remotely, but most of all I MUST know their status.
I saw some answers on the net that are written in VB, but I need it in c#.
the solution given in markdmak answer here is looking like a good start, but it's in VB and it looks for remote sessions only.
I have this piece of code, which can be a start, but I would like to couple the LogonId to a username and to see its status:
string fqdn = ""; // set!!!
ConnectionOptions options = new ConnectionOptions();
options.EnablePrivileges = true;
// To connect to the remote computer using a different account, specify these values:
// these are needed in dev environment
options.Username = ConfigurationManager.AppSettings["KerberosImpersonationUser"];
options.Password = ConfigurationManager.AppSettings["KerberosImpersonationPassword"];
options.Authority = "ntlmdomain:" + ConfigurationManager.AppSettings["KerberosImpersonationDomain"];
ManagementScope scope = new ManagementScope("\\\\" + fqdn + "\\root\\CIMV2", options);
try
{
scope.Connect();
}
catch (Exception ex)
{
if (ex.Message.StartsWith("The RPC server is unavailable"))
{
// The Remote Procedure Call server is unavailable
// cannot check for logged on users
return false;
}
else
{
throw ex;
}
}
SelectQuery query = new SelectQuery("Select * from Win32_LogonSession");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection results = searcher.Get();
bool returnVal = false;
foreach (ManagementObject os in results)
{
try
{
if (os.GetPropertyValue("LogonId").ToString() != null && os.GetPropertyValue("LogonId").ToString() != "")
{
returnVal = true;
}
}
catch (NullReferenceException)
{
continue;
}
}
return returnVal;
}
What I really need and can't find, is a way of getting ALL users on a remote machine AND their status, meaning: Active, Disconnected, Logged-off, etc.
You can use the Win32_LogonSession WMI class filtering for the LogonType property with the value 2 (Interactive)
Try this sample
using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
namespace GetWMI_Info
{
class Program
{
static void Main(string[] args)
{
try
{
string ComputerName = "remote-machine";
ManagementScope Scope;
if (!ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
ConnectionOptions Conn = new ConnectionOptions();
Conn.Username = "username";
Conn.Password = "password";
Conn.Authority = "ntlmdomain:DOMAIN";
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), Conn);
}
else
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null);
Scope.Connect();
ObjectQuery Query = new ObjectQuery("SELECT LogonId FROM Win32_LogonSession Where LogonType=2");
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query);
foreach (ManagementObject WmiObject in Searcher.Get())
{
Console.WriteLine("{0,-35} {1,-40}", "LogonId", WmiObject["LogonId"]);// String
ObjectQuery LQuery = new ObjectQuery("Associators of {Win32_LogonSession.LogonId=" + WmiObject["LogonId"] + "} Where AssocClass=Win32_LoggedOnUser Role=Dependent");
ManagementObjectSearcher LSearcher = new ManagementObjectSearcher(Scope, LQuery);
foreach (ManagementObject LWmiObject in LSearcher.Get())
{
Console.WriteLine("{0,-35} {1,-40}", "Name", LWmiObject["Name"]);
}
}
}
catch (Exception e)
{
Console.WriteLine(String.Format("Exception {0} Trace {1}", e.Message, e.StackTrace));
}
Console.WriteLine("Press Enter to exit");
Console.Read();
}
}
}
#RRUZ got me started but the Associators query did not work on remote machine with a lot of Win32_LoggedOnUser objects (don't know why). No results were returned.
I also needed remote Desktop sessions so I used LogonType "10" sessions and my ConnectionOptions were differents
I replaced the Associators query with WmiObject.GetRelationships("Win32_LoggedOnUser") and the speed increases by a lot and results were there.
private void btnUnleash_Click(object sender, EventArgs e)
{
string serverName = "serverName";
foreach (var user in GetLoggedUser(serverName))
{
dataGridView1.Rows.Add(serverName, user);
}
}
private List<string> GetLoggedUser(string machineName)
{
List<string> users = new List<string>();
try
{
var scope = GetManagementScope(machineName);
scope.Connect();
var Query = new SelectQuery("SELECT LogonId FROM Win32_LogonSession Where LogonType=10");
var Searcher = new ManagementObjectSearcher(scope, Query);
var regName = new Regex(#"(?<=Name="").*(?="")");
foreach (ManagementObject WmiObject in Searcher.Get())
{
foreach (ManagementObject LWmiObject in WmiObject.GetRelationships("Win32_LoggedOnUser"))
{
users.Add(regName.Match(LWmiObject["Antecedent"].ToString()).Value);
}
}
}
catch (Exception ex)
{
users.Add(ex.Message);
}
return users;
}
private static ManagementScope GetManagementScope(string machineName)
{
ManagementScope Scope;
if (machineName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", "."), GetConnectionOptions());
else
{
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", machineName), GetConnectionOptions());
}
return Scope;
}
private static ConnectionOptions GetConnectionOptions()
{
var connection = new ConnectionOptions
{
EnablePrivileges = true,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Impersonate,
};
return connection;
}

WMI defrag method not found in C#

I'm trying to use WMI to defrag my C: Drive.
I'm running Windows 7 Pro x64.
Console.WriteLine(SMARTManager.Instance.SMARTHDD.Defrag("C:", ref ERR));
Function:
public string Defrag(string a_DriveName, ref string ERR)
{
try
{
ManagementObject classInstance =
new ManagementObject("root\\CIMV2",
String.Format("Win32_Volume.DeviceID='{0}'", a_DriveName),
null);
// Obtain in-parameters for the method
ManagementBaseObject inParams =
classInstance.GetMethodParameters("Defrag");
// Add the input parameters.
inParams["Force"] = true;
// Execute the method and obtain the return values.
ManagementBaseObject outParams =
classInstance.InvokeMethod("Defrag", inParams, null);
// List outParams
string callback = "Out parameters:\n" + "ReturnValue: " + outParams["ReturnValue"];
return callback;
}
catch (ManagementException err)
{
ERR = "An error occurred while trying to execute the WMI method: " + err.Message;
}
return null;
}
I got this code from WMI Code Creator but when I run it it returns an exeption saying "Not Found".
Has anyone else tried this?
This error is caused because you are passing a wrong object path to the ManagementObject constructor, a DeviceID looks like \\?\Volume{3a7a882b-8713-11e0-bfc8-806e6f6e6963}\, So to fix your issue you must pass a valid DeviceID or modify your code to use the Name property of the Win32_Volume class.
check this sample code which uses the Name property instead
using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
namespace GetWMI_Info
{
class Program
{
public static void Defrag(string a_DriveName)
{
try
{
string ComputerName = "localhost";
ManagementScope Scope;
if (!ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
ConnectionOptions Conn = new ConnectionOptions();
Conn.Username = "";
Conn.Password = "";
Conn.Authority = "ntlmdomain:DOMAIN";
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), Conn);
}
else
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null);
Scope.Connect();
string WQL = String.Format("SELECT * FROM Win32_Volume Where Name='{0}'", a_DriveName);
ObjectQuery Query = new ObjectQuery(WQL);
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query);
foreach (ManagementObject ClassInstance in Searcher.Get())
{
ManagementBaseObject inParams = ClassInstance.GetMethodParameters("Defrag");
ManagementBaseObject outParams= ClassInstance.InvokeMethod("Defrag", inParams ,null);
Console.WriteLine("{0,-35} {1,-40}","DefragAnalysis",outParams["DefragAnalysis"]);
Console.WriteLine("{0,-35} {1,-40}","ReturnValue",outParams["ReturnValue"]);
}
}
catch (Exception e)
{
Console.WriteLine(String.Format("Exception {0} Trace {1}",e.Message,e.StackTrace));
}
}
static void Main(string[] args)
{
//the drive name must be escaped
Defrag("F:\\\\");
Console.WriteLine("Press Enter to exit");
Console.Read();
}
}

Manage DNS server by C# code

I need some sample code to create/delete zone and A record in microsoft DNS server by C#
You have to use WMI to invoke the DNSProvider.
This to add a record:
public void AddARecord(string hostName, string zone, string iPAddress, string dnsServerName)
{
ManagementScope scope =
new ManagementScope(#"\\" + dnsServerName + "\\root\\MicrosoftDNS");
scope.Connect();
ManagementClass cmiClass =
new ManagementClass(scope,
new ManagementPath("MicrosoftDNS_AType"),
null);
ManagementBaseObject inParams =
cmiClass.GetMethodParameters("CreateInstanceFromPropertyData");
inParams["DnsServerName"] = this.ServerName;
inParams["ContainerName"] = zone;
inParams["OwnerName"] = hostName + "." + zone;
inParams["IPAddress"] = iPAddress;
cmiClass.InvokeMethod("CreateInstanceFromPropertyData", inParams, null);
}
You can reference the WMI reference and extend this as you need using the methods and classes
http://msdn.microsoft.com/en-us/library/ms682123(v=vs.85).aspx
Microsoft exposes it as a POX service, so you could just push XML over the wire to it, using the System.Net stuff & your user credentials.
http://technet.microsoft.com/en-us/library/dd278634.aspx
I agreed with Taylor but in my case i have got 2 different error with above code
1- Generic Error
2- Not Found error
Below code has solved this problems
private ManagementPath UpdateARecord(string strDNSZone, string strHostName, string strIPAddress)
{
ManagementScope mgmtScope = new ManagementScope(#"\\.\Root\MicrosoftDNS");
ManagementClass mgmtClass = null;
ManagementBaseObject mgmtParams = null;
ManagementObjectSearcher mgmtSearch = null;
ManagementObjectCollection mgmtDNSRecords = null;
string strQuery;
strQuery = string.Format("SELECT * FROM MicrosoftDNS_AType WHERE OwnerName = '{0}.{1}'", strHostName, strDNSZone);
mgmtScope.Connect();
mgmtSearch = new ManagementObjectSearcher(mgmtScope, new ObjectQuery(strQuery));
mgmtDNSRecords = mgmtSearch.Get();
//// Multiple A records with the same record name, but different IPv4 addresses, skip.
//if (mgmtDNSRecords.Count > 1)
//{
// // Take appropriate action here.
//}
//// Existing A record found, update record.
//else
if (mgmtDNSRecords.Count == 1)
{
ManagementObject mo = new ManagementObject();
foreach (ManagementObject mgmtDNSRecord in mgmtDNSRecords)
{
if (mgmtDNSRecord["RecordData"].ToString() != strIPAddress)
{
mgmtParams = mgmtDNSRecord.GetMethodParameters("Modify");
mgmtParams["IPAddress"] = strIPAddress;
mgmtDNSRecord.InvokeMethod("Modify", mgmtParams, null);
}
mo = mgmtDNSRecord;
break;
}
return new ManagementPath(mo["RR"].ToString());
}
// A record does not exist, create new record.
else
{
mgmtClass = new ManagementClass(mgmtScope, new ManagementPath("MicrosoftDNS_AType"), null);
mgmtParams = mgmtClass.GetMethodParameters("CreateInstanceFromPropertyData");
mgmtParams["DnsServerName"] = Environment.MachineName;
mgmtParams["ContainerName"] = strDNSZone;
mgmtParams["OwnerName"] = strDNSZone;// string.Format("{0}.{1}", strHostName.ToLower(), strDNSZone);
mgmtParams["IPAddress"] = strIPAddress;
var outParams = mgmtClass.InvokeMethod("CreateInstanceFromPropertyData", mgmtParams, null);
if ((outParams.Properties["RR"] != null))
{
return new ManagementPath(outParams["RR"].ToString());
}
}
return null;
}

Categories

Resources