30

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?

Dana
  • 32,083
  • 17
  • 62
  • 73
Guy
  • 65,082
  • 97
  • 254
  • 325

9 Answers9

43

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);
galets
  • 17,802
  • 19
  • 72
  • 101
  • I have this coded in my project but I have been running in debug mode stepping through it to find why it won't work anymore. I get ".. cannot start Service on computer". InnerException, "The service did not respond to the start or control request in a timely fashion" But it does start in 5 seconds from the management console with the exact same binary!! – John Oct 23 '11 at 21:55
  • Worked for me. If you're attempting this against a remote computer and get an rpc call failed or unable to connect to service control manager on remote computer make sure on your call for WaitForStatus that you pass in a reasonable timespan as the second param (overloaded). I think SC will wait up to 2.5 minutes for a service to start or stop before throwing an exception so I've been using 2 minutes. – Paul Oct 22 '12 at 20:00
  • I'm getting exception as below `An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceProcess.dll Additional information: Cannot open Service Control Manager on computer . This operation might require other privileges.` – venkat Jan 18 '16 at 11:37
  • Assembly reference is plainly "System.ServiceProcess" – Motomotes Jul 21 '16 at 22:33
9

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

Example

sc \\MyServer stop schedule will stop the Scheduler service.

Patrick Cuff
  • 28,540
  • 12
  • 67
  • 94
5

ServiceController.

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

Community
  • 1
  • 1
StingyJack
  • 19,041
  • 10
  • 63
  • 122
3

If you don't want to code it yourself, PsService by Microsoft/Sysinternals is a command line tool that does what you want.

sth
  • 222,467
  • 53
  • 283
  • 367
2

I have done like below:

Note:

  1. If you didn't start your service if you are trying to stop it will throw exception.
  2. If you configure these things in your web.config ,configuration related exception will not come. No need to do anything in IIS.

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.

zx485
  • 28,498
  • 28
  • 50
  • 59
RajeshKdev
  • 6,365
  • 6
  • 58
  • 80
2

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);
Sean
  • 21
  • 2
2

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);
Judy007
  • 5,484
  • 4
  • 46
  • 68
2

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.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
1

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
Community
  • 1
  • 1