I am trying to write a method that will terminate a service on a remote system, by process ID, if it fails to stop using the StopService method. I have tried two different ways of invoking the "Terminate" method on a ManagementObject, and I get two different errors. It's also important to me that I be able to get the return code from the Terminate method.
If I declare a ManagementPath directly to the process I want to terminate, I get the error "System.Management.ManagementException: Invalid object path" at line:
ManagementBaseObject processParams = processObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);
If I get a ManagementObjectCollection and loop through it looking for the Process ID I want to terminate, I get the error "Invalid parameter" at line:
ManagementBaseObject termParams = currentObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);
So, in both cases, I get an error when I try to invoke the Terminate method, but the error differs depending on how I arrive at the object (direct path or loop through collection).
I don't think this is related to SeDebugPrivilege, as I believe I'd be getting "access denied" or "insufficient privilege" if it was.
Code if I attempt to directly specify the path to the process:
public int KillServiceWMI(string serviceName, string serverName, string serverUser, string serverDomain, string serverPassword)
{
try
{
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
options.Username = serverDomain + "\\" + serverUser;
options.Password = serverPassword;
ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\cimv2", options);
Console.WriteLine("Connecting to scope");
scope.Connect();
Console.WriteLine("Getting ManagementPath");
ManagementPath servicePath = new ManagementPath("Win32_Service.Name='" + serviceName + "'");
Console.WriteLine("Getting ManagementObject");
ManagementObject serviceObj = new ManagementObject(scope, servicePath, new ObjectGetOptions());
Console.WriteLine("Name of service is " + serviceObj["DisplayName"].ToString());
Console.WriteLine("Process ID of service is " + serviceObj["ProcessId"].ToString());
ManagementPath processPath = new ManagementPath("Win32_Process.ProcessId='" + serviceObj["ProcessId"] + "'");
ManagementObject processObj = new ManagementObject(scope, processPath, new ObjectGetOptions());
ManagementBaseObject processParams = processObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);
int returnCode = System.Convert.ToInt32(processParams.Properties["ReturnValue"].Value);
return returnCode;
}
catch (Exception connectEx)
{
Console.WriteLine("Connecting to " + serverName + " caused an exception");
Console.Write(connectEx);
return 99;
}
}
Code if I loop through a collection of processes:
public int KillServiceWMI(string serviceName, string serverName, string serverUser, string serverDomain, string serverPassword)
{
try
{
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
options.Username = serverDomain + "\\" + serverUser;
options.Password = serverPassword;
ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\cimv2", options);
Console.WriteLine("Connecting to scope");
scope.Connect();
Console.WriteLine("Getting ManagementPath");
ManagementPath servicePath = new ManagementPath("Win32_Service.Name='" + serviceName + "'");
Console.WriteLine("Getting ManagementObject");
ManagementObject serviceObj = new ManagementObject(scope, servicePath, new ObjectGetOptions());
Console.WriteLine("Name of service is " + serviceObj["DisplayName"].ToString());
Console.WriteLine("Process ID of service is " + serviceObj["ProcessId"].ToString());
ObjectQuery serviceQuery = new ObjectQuery("SELECT * from Win32_Process WHERE ProcessID = '" + serviceObj["ProcessId"].ToString() + "'");
ManagementObjectSearcher serviceSearcher = new ManagementObjectSearcher(scope, serviceQuery);
ManagementObjectCollection serviceColl = serviceSearcher.Get();
int returnCode = 0;
foreach (ManagementObject currentObj in serviceColl)
{
if (currentObj["ProcessId"].ToString().Equals(serviceObj["ProcessId"].ToString(), StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Found process " + currentObj["ProcessId"].ToString() + ". Terminating...");
ManagementBaseObject termParams = currentObj.InvokeMethod("Terminate", (ManagementBaseObject)null, null);
returnCode = System.Convert.ToInt32(termParams.Properties["ReturnValue"].Value);
}
}
return returnCode;
}
catch (Exception connectEx)
{
Console.WriteLine("Connecting to " + vaultName + " caused an exception");
Console.Write(connectEx);
return 99;
}
}
I eventually gave up trying to use the Terminate method on Win32_Process, and instead I'm using Create to call TaskKill.exe remotely. Because the return info is now hidden behind taskkill.exe, I then have to get the process list again and look for the target pid to make sure that the process was actually terminated.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
options.Username = serverDomain + "\\" + serverUser;
options.Password = serverPassword;
ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\cimv2", options);
Console.WriteLine("Connecting to scope");
scope.Connect();
Console.WriteLine("Getting ManagementPath");
ManagementPath servicePath = new ManagementPath("Win32_Service.Name='" + serviceName + "'");
Console.WriteLine("Getting ManagementObject");
ManagementObject serviceObj = new ManagementObject(scope, servicePath, new ObjectGetOptions());
Console.WriteLine("Name of service is " + serviceObj["DisplayName"].ToString());
Console.WriteLine("Process ID of service is " + serviceObj["ProcessId"].ToString());
// use processid to kill process with taskkill
ObjectGetOptions processObjGetOpt = new ObjectGetOptions();
ManagementPath processPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(scope, processPath, processObjGetOpt);
ManagementBaseObject processInParams = processClass.GetMethodParameters("Create");
processInParams["CommandLine"] = string.Format("cmd /c \"taskkill /f /pid {0}\"", serviceObj["ProcessId"].ToString());
ManagementBaseObject outParams = processClass.InvokeMethod("Create", processInParams, null);
Console.WriteLine("Return code for taskkill: " + outParams["returnValue"]);
int returnCode = System.Convert.ToInt32(outParams["returnValue"]);
Related
I am an administrator and want to delegate helpdesk stuff to do some applications silent installs which requires admin. rights, I tried below code and always gets error message 'RPC server is unavailable', I cannot figure out what's wrong with my code, any help will be highly appreciated.
public static string ahmed(string machine, string username, string password, string domain)
{
try
{
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.Authority = "ntlmdomain:" + domain + #"\" + machine;
connectionOptions.Username = username;
connectionOptions.Password = password;
connectionOptions.Impersonation = ImpersonationLevel.Delegate;
connectionOptions.Authentication = AuthenticationLevel.PacketPrivacy;
//define the WMI root name space
ManagementScope scope = new ManagementScope(#"\\" + machine + "." + domain + #"\root\CIMV2", connectionOptions);
//define path for the WMI class
ManagementPath p = new ManagementPath("Win32_Process");
//define new instance
ManagementClass processClass = new ManagementClass(scope, p, null);
// Create an array containing all
// arguments for the method
object[] methodArgs =
{#"mkdir c:\testtest", null, null, 0};
//Execute the method
object result =
processClass.InvokeMethod(
"Create", methodArgs);
return "done";
}
catch (ManagementException me)
{
return me.Message;
}
catch (COMException ioe)
{
return ioe.Message;
}
}
The code uses the wmi component in semi-synchronous mode to monitor events on a remote computer.
Everything works correctly until the network connection between the client and the server is interrupted.
In this case the program remains frozen in the line:
ManagementBaseObject e = watcher.WaitForNextEvent ();
and the timeout does not work.
How can I manage the connection break correctly?
Thank you
static void Main(string[] args)
{
ConnectionOptions con1 = new ConnectionOptions();
con1.Username = networkUser;
con1.Password = networkPassword;
con1.Impersonation = ImpersonationLevel.Impersonate;
con1.EnablePrivileges = true;
con1.Authentication = AuthenticationLevel.PacketPrivacy;
ManagementPath mp1 = new ManagementPath(#"\\" + networkPath + #"\" + #"root\cimv2");
try
{
string queryString =
"SELECT * " +
"FROM __InstanceOperationEvent " +
"WITHIN 5 " +
"WHERE TargetInstance ISA 'Win32_Process' ";
WqlEventQuery wql = new WqlEventQuery(queryString);
ManagementScope Scope = new ManagementScope(mp1, con1);
Scope.Connect();
// create the watcher and start to listen
ManagementEventWatcher watcher = new ManagementEventWatcher();
watcher.Query = wql;
watcher.Scope = Scope;
watcher.Options.Timeout = TimeSpan.FromSeconds(5);
keepGoing = true;
int nError = 0;
while (keepGoing)
{
try
{
ManagementBaseObject e = watcher.WaitForNextEvent();
switch (e.ClassPath.ClassName)
{
case ("__InstanceDeletionEvent"):
{
keepGoing = false;
break;
}
default:
{
Console.WriteLine("Evento non gestito : " + e.ClassPath.ClassName);
break;
}
}
}
catch (System.Management.ManagementException e)
{
if (e.ErrorCode != ManagementStatus.Timedout)
throw;
}
}
watcher.Stop();
}
catch (Exception ex)
{
Console.WriteLine("Errore: " + ex.Message);
Console.ReadLine();
}
}
I answer myself.
Setting the watcher timeout to:
watcher.Options.Timeout = new TimeSpan (0, 0, 0, 0, 1);
I programmed a project to manage Dns. I could code for create and delete Zone, NsRecord and ARecord, but I can't get ARecords of the Zone.
Can anyone guide me?
This is sample of my code:
private void CreateZone(string domainName)
{
wmiScope = new ManagementScope("\\\\" + System.Environment.MachineName + "\\ROOT\\MicrosoftDNs");
wmiScope.Connect();
var zonePath = new ManagementPath("MicrosoftDNs_Zone");
var zone = new ManagementClass(wmiScope, zonePath, null);
var inParams = zone.GetMethodParameters("CreateZone");
inParams.Properties["ZoneName"].Value = domainName;
inParams.Properties["ZoneType"].Value = 0;
zone.InvokeMethod("CreateZone", inParams, null);
var query = new ObjectQuery("SELECT * FROM MicrosoftDNs_SOAType WHERE OwnerName = '" + domainName + "'");
var searcher = new ManagementObjectSearcher(wmiScope, query);
var zoneRecordes = searcher.Get();
foreach (ManagementObject zoneRecorde in zoneRecordes)
{
var soaParams = zoneRecorde.GetMethodParameters("Modify");
soaParams.Properties["PrimaryServer"].Value = "ns1.domain.com";
soaParams.Properties["ResponsibleParty"].Value = "contact#domain.com";
zoneRecorde.InvokeMethod("Modify", soaParams, null);
}
}
To be complete I'll add the answer that got us to the solution.
According to the following site (discuss.fogcreek.com/dotnetquestions/…) you should be able to get the ARecords like this:
ManagementScope oMs = new ManagementScope("\\\\" + dnsServer + "\\root\\microsoftdns");
string strQuery = "select * from microsoftdns_" + recType + "type where containername = '" + domain + "'";
ManagementObjectSearcher oS = new ManagementObjectSearcher(strQuery); oS.Scope = oMs;
ManagementObjectCollection oRc = oS.Get();
I want to use c# and WMI to start a process remotely in another computer. I've made some initial research and found out that i ultimately have to use a processclass. The "Win32_Process" was the first thing that seemed obvious to be used, however, it seems it is limited to represent only local processes. What other Windows process classes can I use?
Here is what the code when using Win32_ScheduledJob class:
static public String RemoteConnect()
{
try
{
ConnectionOptions conn = new ConnectionOptions();
conn.Username = #"JV";
conn.Password = #"Nazpal6180";
conn.EnablePrivileges = true;
conn.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope scope = new ManagementScope("\\\\phsd194-JV\\root\\cimv2", conn);
//scope.Options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
//scope.Options.EnablePrivileges = true;
scope.Connect();
ManagementPath managementPath = new ManagementPath("Win32_ScheduledJob");
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementClass classInstance = new ManagementClass(scope, managementPath, objectGetOptions);
object[] objectsIn = new object[7];
objectsIn[0] = "calc.exe";
objectsIn[1] = "********140000.000000+480";
objectsIn[5] = true;
object outParams = classInstance.InvokeMethod("Create", objectsIn);
String response = "Creation of the process returned: " + outParams;
return response;
}
catch (ManagementException err)
{
String response = "An error occurred while trying to execute the WMI method: " + err.Message;
//Console.WriteLine("An error occurred while trying to execute the WMI method: " + err.Message);
return response;
}
}
As you point in your comments, the Win32_Process.Create method cannot be used to start an interactive process remotely, so as workaround you can use the Win32_ScheduledJob class with the Create method.
Check this sample app, which start the notepad in a remote machine in one minute , (Assuming which the time of the remote machine is the same of the local machine, if not you can get the local time using Win32_LocalTime or Win32_UtcTime from the remote machine and then convert to UTC).
using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
namespace ConsoleApplication11
{
class Program
{
private static string DateTimetoUTC(DateTime dateParam)
{
string buffer = dateParam.ToString("********HHmmss.ffffff");
TimeSpan tickOffset = TimeZone.CurrentTimeZone.GetUtcOffset(dateParam);
buffer += (tickOffset.Ticks >= 0) ? '+' : '-';
buffer += (Math.Abs(tickOffset.Ticks) / System.TimeSpan.TicksPerMinute).ToString("d3");
return buffer;
}
static void Main(string[] args)
{
try
{
ConnectionOptions conn = new ConnectionOptions();
conn.Username = "theusername";
conn.Password = "password";
//connectoptions.Authority = "ntlmdomain:";
conn.EnablePrivileges = true;
ManagementScope scope = new ManagementScope(#"\\192.168.52.128\root\cimv2", conn);
scope.Connect();
Console.WriteLine("Connected");
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_ScheduledJob");
ManagementClass classInstance = new ManagementClass(scope, managementPath, objectGetOptions);
ManagementBaseObject inParams = classInstance.GetMethodParameters("Create");
inParams["Command"] = #"notepad.exe";
//the itme must be in UTC format
string StartTime = DateTimetoUTC(DateTime.Now.AddMinutes(1));
Console.WriteLine(StartTime);
inParams["StartTime"] = StartTime;
ManagementBaseObject outParams = classInstance.InvokeMethod("Create", inParams, null);
Console.WriteLine("JobId: " + outParams["JobId"]);
Console.ReadKey();
}
catch(ManagementException err)
{
Console.WriteLine("An error occurred while trying to execute the WMI method: " + err.Message);
Console.ReadKey();
}
}
}
}
I believe C# has just a Process class. I've used it for starting remote processes before.
I'd go for a server/client architecture where the server can start processes based on some kind of network call.
Is there a way to determine what all users are logged into remote machine, using WMI and C#
After little research I was able to figure it out, although not sure if this is the best way
public void GetCompDet(string ComputerName)
{
CurrentSystem = ComputerName;
ConnectionOptions options = new ConnectionOptions();
ManagementScope moScope = new ManagementScope(#"\\" + ComputerName + #"\root\cimv2");
try
{
moScope.Connect();
}
catch
{
return;
}
ObjectQuery query = new ObjectQuery("select * from Win32_Process where name='explorer.exe'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(moScope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
ManagementOperationObserver mo = new ManagementOperationObserver();
mo.ObjectReady += new ObjectReadyEventHandler(mo_ObjectReady);
m.InvokeMethod(mo, "GetOwner", null);
}
}
void mo_ObjectReady(object sender, ObjectReadyEventArgs e)
{
ManagementObject m = sender as ManagementObject;
LoggedinUser.Enqueue(CurrentSystem + " - >" + e.NewObject.Properties["user"].Value.ToString());
Console.WriteLine(CurrentSystem + " - >" + e.NewObject.Properties["user"].Value.ToString());
}