I want to write a console or Click Once WinForms app that will programmatically stop and/or start a windows service on a remote box.
Both boxes are running .NET 3.5 - what .NET API's are available to accomplish this?
I want to write a console or Click Once WinForms app that will programmatically stop and/or start a windows service on a remote box.
Both boxes are running .NET 3.5 - what .NET API's are available to accomplish this?
in C#:
var sc = new System.ServiceProcess.ServiceController("MyService", "MyRemoteMachine");
sc.Start();
sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);
sc.Stop();
sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);
You can also do this from a command console using the sc
command:
sc <server> start [service name]
sc <server> stop [service name]
Use
sc <server> query | find "SERVICE_NAME"
to get a list of service names.
The option <server>
has the form \\ServerName
sc \\MyServer stop schedule
will stop the Scheduler service.
You need to have permission to administer the services on the remote box.
As Mehrdad says, you can also use WMI. Both methods work for start and stop, but WMI requires more coding and will give you more access to other resources
I have done like below:
Note:
In Web.Config
under <configuration>
<appSettings>
<add key="ServiceName" value="YourServiceName" />
<add key="MachineName" value="YourMachineName" />
</appSettings>
<system.web>
<authentication mode="Windows"/>
<identity impersonate="true" userName="YourUserName" password="YourPassword"/>
</system.web>
In My Service Class:
private void RestartService()
{
string serviceName = System.Configuration.ConfigurationSettings.AppSettings["ServiceName"];
string machineName = System.Configuration.ConfigurationSettings.AppSettings["MachineName"];
try
{
var service = new ServiceController(serviceName, machineName);
if (service.Status != ServiceControllerStatus.Stopped)
{
service.Stop();
service.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);
}
service.Start();
service.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);
}
catch (Exception)
{
}
}
Hope this Helps.
if you need to get the name of the Service:
run this from the command line:
sc query
You will see for example, that SQL Server's service name is 'MSSQL$SQLEXPRESS'.
So to stop the SQL Server service in C#:
ServiceController controller = new ServiceController();
controller.MachineName = "Machine1";
controller.ServiceName = "MSSQL$SQLEXPRESS";
if(controller.Status == ServiceControllerStatus.Running)
controller.Stop();
controller.WaitForStatus(ServiceControllerStatus.Stopped);
galets code snippet above is a great start. However, keep in mind it assumes that the service has already started, or, more importantly, that
sc.Status == System.ServiceProcess.ServiceControllerStatus.Running
Also, it may important to, at some point during code execution, call
sc.Refresh();
because the properties values (such as ServiceControllerStatus) may not reflect the actual properties of the service. For instance, you may call
sc.Start();
and wait indefinitely when this command executes
sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running)
Here is a version of this code that I coded with those considerations in mind.
//Restart Content Service on DEV.
String svcName = "TheServiceName";
String machineName = "TheMachineName";
var sc = new System.ServiceProcess.ServiceController(svcName, machineName);
Console.WriteLine("Stopping Service '{0}' on machine '{1}", svcName, machineName);
sc.Stop();
sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);
//sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);
do
{
try
{
sc.Refresh();
if (sc.Status == System.ServiceProcess.ServiceControllerStatus.Running)
{
Console.WriteLine("Code has detected that servive start is pending, waiting 5 seconds to see if status changes..");
System.Threading.Thread.Sleep(5000);
}
else
{
Console.WriteLine("waiting 5 seconds and retrying start..");
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Attempt Starting Service '{0}' on machine '{1}", svcName, machineName);
sc.Start();
}
}
catch(Exception ex)
{
//If it is already running, then abort do while
if (ex.InnerException.Message == "An instance of the service is already running")
{
Console.WriteLine(ex.InnerException.Message);
continue;
}
Console.WriteLine(ex.InnerException.ToString());
}
} while (sc.Status != System.ServiceProcess.ServiceControllerStatus.Running);
You can use System.Management
APIs (WMI) to control services remotely. WMI is the generic API to do administrative tasks.
For this problem, however, I suggest you to use the easier to use System.ServiceProcess.ServiceController
class.
Here is a ServiceExtension that can Start and Stop Services on remote pc's.
And it can set the Startup type of the service, even to "automatic (delayed)"
modified version from this Answer to work on remote machines.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;
namespace Helpers
{
public enum ServiceStartModeEx
{
Automatic = 2,
Manual = 3,
Disabled = 4,
DelayedAutomatic = 99
}
/// <summary>
/// Extensions to the ServiceController class.
/// </summary>
public static class ServiceControlerExtensions
{
/// <summary>
/// Set the start mode for the service.
/// </summary>
/// <param name="serviceController">The service controller.</param>
/// <param name="mode">The desired start mode.</param>
public static void SetStartMode(this ServiceController serviceController, ServiceStartModeEx mode)
{
IntPtr serviceManagerHandle = OpenServiceManagerHandle(serviceController);
IntPtr serviceHandle = OpenServiceHandle(serviceController, serviceManagerHandle);
try
{
if (mode == ServiceStartModeEx.DelayedAutomatic)
{
ChangeServiceStartType(serviceHandle, ServiceStartModeEx.Automatic);
ChangeDelayedAutoStart(serviceHandle, true);
}
else
{
// Delayed auto-start overrides other settings, so it must be set first.
ChangeDelayedAutoStart(serviceHandle, false);
ChangeServiceStartType(serviceHandle, mode);
}
}
finally
{
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceHandle);
}
if (serviceManagerHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceManagerHandle);
}
}
}
private static IntPtr OpenServiceHandle(ServiceController serviceController, IntPtr serviceManagerHandle)
{
var serviceHandle = OpenService(
serviceManagerHandle,
serviceController.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
return serviceHandle;
}
private static IntPtr OpenServiceManagerHandle(ServiceController serviceController)
{
var machineName = string.IsNullOrWhiteSpace(serviceController.MachineName)
? null
: serviceController.MachineName;
IntPtr serviceManagerHandle = OpenSCManager(machineName, null, SC_MANAGER_ALL_ACCESS);
if (serviceManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
return serviceManagerHandle;
}
private static void ChangeServiceStartType(IntPtr serviceHandle, ServiceStartModeEx mode)
{
bool result = ChangeServiceConfig(
serviceHandle,
SERVICE_NO_CHANGE,
(uint)mode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
ThrowLastWin32Error("Could not change service start type");
}
}
private static void ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Create structure that contains DelayedAutoStart property.
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property in that structure.
info.fDelayedAutostart = delayed;
// Allocate necessary memory.
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer.
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration.
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory.
Marshal.FreeHGlobal(hInfo);
if (result == false)
{
ThrowLastWin32Error("Could not set service to delayed automatic");
}
}
private static void ThrowLastWin32Error(string messagePrefix)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
string message = string.Format("{0}: {1}", messagePrefix, win32Exception.Message);
throw new ExternalException(message);
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr OpenService(
IntPtr hSCManager,
string lpServiceName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
SetLastError = true)]
private static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(
IntPtr hService,
int dwInfoLevel,
IntPtr lpInfo);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
private static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
private const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
}
}
You can start a service like this
using System.ServiceProcess;
serviceName = "the name of the service"
machineName = "the name of the remote/local host"
var service = new ServiceController(serviceName, machineName);
try
{
service.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
service.Start();
}
finally
{
service.Close();
}
You can stop a service like this
var service = new ServiceController(serviceName, machineName);
try
{
if (service.CanStop)
{
service.SetStartMode(ServiceStartModeEx.Disabled);
service.Stop();
}
}
finally
{
service.Close();
}
To grant a user rights to start and stop a service on a remote pc you have to set some service rights, you can google what subinacl.exe is and where to download it.
C:\Program Files (x86)\Windows Resource Kits\Tools>subinacl.exe /service SERVICENAME /grant=MACHINENAME\USERNAME=F