How to restart service remotely? - c#

I can start or stop service remotely from .net project.
ConnectionOptions options = new ConnectionOptions();
options.Username = #"192.168.36.22\test";
options.Password = "test";
ManagementScope scope = new ManagementScope(#"\\192.168.36.22\root\cimv2", options);
scope.Connect();
ManagementOperationObserver Stop = new ManagementOperationObserver();
Stop.Completed += new CompletedEventHandler(Stop_CallBack);
try
{
string NameServices = "ArcGIS Server";
WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_Service WHERE Name=\"" + NameServices + "\"");
ManagementObjectSearcher find = new ManagementObjectSearcher(scope, query);
foreach (ManagementObject spooler in find.Get())
{
spooler.InvokeMethod("StopService", new object[] { });
spooler.InvokeMethod(Start, "StopService", new object[] { });
}
}
....
How can I restart this service?

You could use the ServiceController class like so:
ServiceController sc = new ServiceController("ArcGIS Server", "192.168.36.22");
sc.Start();
sc.Stop();
This saves you having to write all that code to interact with WMI. Note to use the ServiceController class, you'll have to add a reference to the System.ServiceProcess assembly.

Service controller didn't work for me, so I used Cmd to do it.
Process.Start("CMD.exe", "/C sc \\\\remoteMachine stop \"serviceName\"&sc \\\\remoteMachine start \"serviceName\"");
To overcome credentials issue, I used class from this https://stackoverflow.com/a/5433640/2179222 answer.
So in the end It looked like this:
private static void RestartService(string remoteMachine, string serviceName, string userName, string password)
{
using (new NetworkConnection($"\\\\{remoteMachine}", new NetworkCredential(userName, password)))
{
Process.Start("CMD.exe", $"/C sc \\\\{remoteMachine} stop \"{serviceName}\"&sc \\\\{remoteMachine} start \"{serviceName}\"");
}
}

I have come across a similar problem when I tried to connect, just add your machine name as admin in the 'users' group of the target machine and you will be able to fetch the data.

Related

Problem starting an exe file on remote machine using wmi

I followed some articles here and managed to start notepad.exe on a remote machine.
I am trying to start a third-party software we bought and installed on the remote machine once it restarts, but it's not running the exe..
object[] theProcessToRun = { #"C:\Program Files (x86)\xyz\xyz.exe" };
var connection = new ConnectionOptions
{
Username = "username",
Password = "password",
Authority = ""ntlmdomain:.local"",
};
var scope = new ManagementScope("\\\\IP\\root\\CIMV2", connection);
scope.Connect();
using (var managementClass = new ManagementClass(scope, new ManagementPath("Win32_Process"), new ObjectGetOptions()))
{
managementClass.InvokeMethod("Create", theProcessToRun);
}

Is a process running on a remote machine?

I have three remote PC's to which I remotely connect. I am trying to write a simple Windows application that would display in a single window whether a particular process is running on either of the machines, e.g.
Server1: Chrome not running
Server2: Chrome IS running
Server3: Chrome IS running
I used WMI and C#. So far I've got this much:
ConnectionOptions connectoptions = new ConnectionOptions();
connectoptions.Username = #"domain\username";
connectoptions.Password = "password";
//IP Address of the remote machine
string ipAddress = "192.168.0.217";
ManagementScope scope = new ManagementScope(#"\\" + ipAddress + #"\root\cimv2");
scope.Options = connectoptions;
//Define the WMI query to be executed on the remote machine
SelectQuery query = new SelectQuery("select * from Win32_Process");
using (ManagementObjectSearcher searcher = new
ManagementObjectSearcher(scope, query))
{
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject process in collection)
{
// dwarfs stole the code!! :'(
}
}
I think it is all set up correctly, but if I MessageBox.Show(process.ToString()) inside the foreach loop, I get a whole bunch of message boxes with the following text:
\\username\root\cimv2:W32_Process.Handle="XXX"
I am kind of stuck. Is there any way I can "translate" that XXX to a process name? Or else, how can actually get the names of the processes so I can use an if statement to check whether it is a "chrome" process?
Or...is my implementation an overkill? Is there an easier way to accomplish this?
Thanks a lot!
In your foreach, try this:
Console.WriteLine(process["Name"]);
You can filter the name of the process to watch in the WQL sentence, so you can write something like this
SelectQuery query = new SelectQuery("select * from Win32_Process Where Name='Chrome.exe'");
Try this sample app
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 = "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();
ObjectQuery Query = new ObjectQuery("SELECT * FROM Win32_Process Where Name='Chrome.exe'");
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query);
foreach (ManagementObject WmiObject in Searcher.Get())
{
//for each instance found, do something
Console.WriteLine("{0,-35} {1,-40}","Name",WmiObject["Name"]);
}
}
catch (Exception e)
{
Console.WriteLine(String.Format("Exception {0} Trace {1}",e.Message,e.StackTrace));
}
Console.WriteLine("Press Enter to exit");
Console.Read();
}
}
}
Try Process.GetProcesses("chrome", "computerName");
Defined in System.Diagnostics.Process as
public static Process[] GetProcessesByName(
string processName,
string machineName)

C# WMI Win32_ScheduledJob properties

ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Username = _username;
connOptions.Password = _password;
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.Authentication = AuthenticationLevel.PacketPrivacy;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(_server, connOptions);
manScope.Connect();
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_ScheduledJob");
ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["Name"] = "TESTER";
inParams["Owner"] = "Tester";
inParams["Command"] = command;
inParams["StartTime"] = "********171000.000000-300";
I'm tyring to connect to a remote system to create a scheduled task. I can create the scheduled tasks, but its being created with user - SYSTEM. I want it to be created under my user. I tried using the properties like 'Owner' and 'Name' eg:
inParams["Owner"] = ;
inParams["Name"] = ;
But they throw a ManagementException, "Not Found". Does anyone know how I can do this, or what might be wrong that I'm doing here...
Thanks
Creating a scheduled job with the Win32_ScheduledJob WMI class is equivalent to create job using the AT command. The AT service normally run under the Local\System account or the NetworkService Account. so when you uses this class your jobs are created using one of these accounts for more info about this topic you can check the remarks part of the MSDN documentation.

C# WMI privileges

I have searched through numerous questions about how to convert VBS to C# and all that good stuff.
The issue that I'm having is that with the VBS code (see below) when the process is executed on the remote machine it's run with the SYSTEM account.
When I execute with C# it's run with my credentials (or whomever runs the C# program).
The VBS seems to be more reliable in getting remote installs to go through which is what i need it for.
I wanted to switch to C# so I could make a more user friendly GUI for the program.
Anyone know how to get C# to run WMI with the SYSTEM account?
VBS Code:
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objScheduledJob = objWMIService.Get("Win32_ScheduledJob")
Set objSWbemDateTime = CreateObject("WbemScripting.SWbemDateTime")
'add the scheduled job to be run
objSWbemDateTime.SetVarDate(DateAdd(INTERVAL, MINUTES, Now()))
errReturn = objScheduledJob.Create(strCommand, objSWbemDateTime.Value, False, 0, 0, True, intJobID)
C# code:
static public int RemoteAdmin(string remoteMachine, string runFile)
{
try
{
ManagementPath run = new ManagementPath(#"\\" + remoteMachine + #"\root\cimv2:Win32_process");
ManagementClass man = new ManagementClass(run);
man.InvokeMethod("Create", new Object[] { runFile });
return 0;
}
catch
{
MessageBox.Show("Error in remote execution");
return 1;
}
}
You need to setup the ConnectionOptions
ConnectionOptions wmiConnOpts = new ConnectionOptions();
wmiConnOpts.Impersonation = ImpersonationLevel.Impersonate;
wmiConnOpts.Authentication = System.Management.AuthenticationLevel.Default;
wmiConnOpts.EnablePrivileges = true;
ManagementScope wmiLoc =
new ManagementScope(String.Format(#"\\{0}\root\cimv2", remoteMachine ),
wmiConnOpts);
ManagementPath wmiProcPath = new ManagementPath("Win32_ScheduledJob");
ManagementClass wmiProc = new ManagementClass(wmiLoc, wmiProcPath, null);
wmiProc.InvokeMethod("Create", new Object[] { runFile });
This is probably idiot error on my part but the error I was getting was because I didn't specify a time (UTC format) for it to start the scheduled job
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.Authentication = AuthenticationLevel.PacketPrivacy;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(String.Format(#"\\" + remoteMachine + #"\ROOT\CIMV2"), connOptions);
manScope.Connect();
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_ScheduledJob");
ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["Command"] = runFile;
string StartTime = DateTimetoUTC(DateTime.Now.AddMinutes(1));
inParams["StartTime"] = StartTime;
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);

How do I change a Windows Service's startup type in .NET (post-install)?

I have a program that installs a service, and I'd like to be able to give the user the option later on to change the startup type to "Automatic".
The OS is XP - if it makes any difference (Windows APIs?).
How can I do this in .NET? C# if possible! :)
I wrote a blog post on how to do this using P/Invoke. Using the ServiceHelper class from my post you can do the following to change the Start Mode.
var svc = new ServiceController("ServiceNameGoesHere");
ServiceHelper.ChangeStartMode(svc, ServiceStartMode.Automatic);
In the service installer you have to say
[RunInstaller(true)]
public class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
...
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
}
}
You could also ask the user during installation and then set this value. Or just set this property in the visual studio designer.
You can use the OpenService() and ChangeServiceConfig() native Win32 APIs for that purpose. I believe that there is some information on pinvoke.net and of course on MSDN. You might want to check out the P/Invoke Interopt Assistant.
You can use WMI to query all services and then match the service name to the inputted user value
Once the service has been found just change the StartMode Property
if(service.Properties["Name"].Value.ToString() == userInputValue)
{
service.Properties["StartMode"].Value = "Automatic";
//service.Properties["StartMode"].Value = "Manual";
}
//This will get all of the Services running on a Domain Computer and change the "Apple Mobile Device" Service to the StartMode of Automatic. These two functions should obviously be separated, but it is simple to change a service start mode after installation using WMI
private void getServicesForDomainComputer(string computerName)
{
ConnectionOptions co1 = new ConnectionOptions();
co1.Impersonation = ImpersonationLevel.Impersonate;
//this query could also be: ("select * from Win32_Service where name = '" + serviceName + "'");
ManagementScope scope = new ManagementScope(#"\\" + computerName + #"\root\cimv2");
scope.Options = co1;
SelectQuery query = new SelectQuery("select * from Win32_Service");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject service in collection)
{
//the following are all of the available properties
//boolean AcceptPause
//boolean AcceptStop
//string Caption
//uint32 CheckPoint
//string CreationClassName
//string Description
//boolean DesktopInteract
//string DisplayName
//string ErrorControl
//uint32 ExitCode;
//datetime InstallDate;
//string Name
//string PathName
//uint32 ProcessId
//uint32 ServiceSpecificExitCode
//string ServiceType
//boolean Started
//string StartMode
//string StartName
//string State
//string Status
//string SystemCreationClassName
//string SystemName;
//uint32 TagId;
//uint32 WaitHint;
if(service.Properties["Name"].Value.ToString() == "Apple Mobile Device")
{
service.Properties["StartMode"].Value = "Automatic";
}
}
}
}
I wanted to improve this response... One method to change startMode for Specified computer, service:
public void changeServiceStartMode(string hostname, string serviceName, string startMode)
{
try
{
ManagementObject classInstance =
new ManagementObject(#"\\" + hostname + #"\root\cimv2",
"Win32_Service.Name='" + serviceName + "'",
null);
// Obtain in-parameters for the method
ManagementBaseObject inParams = classInstance.GetMethodParameters("ChangeStartMode");
// Add the input parameters.
inParams["StartMode"] = startMode;
// Execute the method and obtain the return values.
ManagementBaseObject outParams = classInstance.InvokeMethod("ChangeStartMode", inParams, null);
// List outParams
//Console.WriteLine("Out parameters:");
//richTextBox1.AppendText(DateTime.Now.ToString() + ": ReturnValue: " + outParams["ReturnValue"]);
}
catch (ManagementException err)
{
//richTextBox1.AppendText(DateTime.Now.ToString() + ": An error occurred while trying to execute the WMI method: " + err.Message);
}
}
How about make use of c:\windows\system32\sc.exe to do that ?!
In VB.NET Codes, use System.Diagnostics.Process to call sc.exe to change the startup mode of a windows service. following is my sample code
Public Function SetStartModeToDisabled(ByVal ServiceName As String) As Boolean
Dim sbParameter As New StringBuilder
With sbParameter
.Append("config ")
.AppendFormat("""{0}"" ", ServiceName)
.Append("start=disabled")
End With
Dim processStartInfo As ProcessStartInfo = New ProcessStartInfo()
Dim scExeFilePath As String = String.Format("{0}\sc.exe", Environment.GetFolderPath(Environment.SpecialFolder.System))
processStartInfo.FileName = scExeFilePath
processStartInfo.Arguments = sbParameter.ToString
processStartInfo.UseShellExecute = True
Dim process As Process = process.Start(processStartInfo)
process.WaitForExit()
Return process.ExitCode = 0
End Function
In ProjectInstaller.cs, click/select the Service1 component on the design surface. In the properties windo there is a startType property for you to set this.
ServiceInstaller myInstaller = new System.ServiceProcess.ServiceInstaller();
myInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
You can do it in the Installer class for the service by setting ServiceInstaller.StartType property to whatever value you get (you'll probably have to do this in a custom action since you want the user to specify) or you can modify the Service's "Start" REG_DWORD entry, the value 2 is automatic and 3 is manual. Its in HKEY_LOCAL_MACHINE\SYSTEM\Services\YourServiceName
One way would be to uninstall previous service and install new one with updated parameters directly from your C# application.
You will need WindowsServiceInstaller in your app.
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
public WindowsServiceInstaller()
{
ServiceInstaller si = new ServiceInstaller();
si.StartType = ServiceStartMode.Automatic; // get this value from some global variable
si.ServiceName = #"YOUR APP";
si.DisplayName = #"YOUR APP";
this.Installers.Add(si);
ServiceProcessInstaller spi = new ServiceProcessInstaller();
spi.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
spi.Username = null;
spi.Password = null;
this.Installers.Add(spi);
}
}
and to reinstall service just use these two lines.
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });

Categories