namespace System.ServiceProcess
{
- /// This class represents an NT service. It allows you to connect to a running or stopped service
- /// and manipulate it or get information about it.
- [Designer("System.ServiceProcess.Design.ServiceControllerDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
- public class ServiceController : Component
- {
- private string _machineName; // Never null
- private const string DefaultMachineName = ".";
-
- private string? _name;
- private string? _eitherName;
- private string? _displayName;
-
- private int _commandsAccepted;
- private bool _statusGenerated;
- private bool _startTypeInitialized;
- private int _type;
- private bool _disposed;
- private SafeServiceHandle? _serviceManagerHandle;
- private ServiceControllerStatus _status;
- private ServiceController[]? _dependentServices;
- private ServiceController[]? _servicesDependedOn;
- private ServiceStartMode _startType;
-
- public ServiceController()
- {
- _machineName = DefaultMachineName;
- _type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
- }
+ /// This class represents an NT service. It allows you to connect to a running or stopped service
+ /// and manipulate it or get information about it.
+ [Designer("System.ServiceProcess.Design.ServiceControllerDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+ public class ServiceController : Component
+ {
+ private string _machineName; // Never null
+ private const string DefaultMachineName = ".";
+
+ private string? _name;
+ private string? _eitherName;
+ private string? _displayName;
+
+ private int _commandsAccepted;
+ private bool _statusGenerated;
+ private bool _startTypeInitialized;
+ private int _type;
+ private bool _disposed;
+ private SafeServiceHandle? _serviceManagerHandle;
+ private ServiceControllerStatus _status;
+ private ServiceController[]? _dependentServices;
+ private ServiceController[]? _servicesDependedOn;
+ private ServiceStartMode _startType;
+
+ public ServiceController()
+ {
+ _machineName = DefaultMachineName;
+ _type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
+ }
- /// <summary>
- /// Creates a ServiceController object, based on service name.
- /// </summary>
- /// <param name="name">Name of the service</param>
- public ServiceController(string name)
- : this(name, DefaultMachineName)
- {
- }
+ /// <summary>
+ /// Creates a ServiceController object, based on service name.
+ /// </summary>
+ /// <param name="name">Name of the service</param>
+ public ServiceController(string name)
+ : this(name, DefaultMachineName)
+ {
+ }
- /// <summary>
- /// Creates a ServiceController object, based on machine and service name.
- /// </summary>
- /// <param name="name">Name of the service</param>
- /// <param name="machineName">Name of the machine</param>
- public ServiceController(string name, string machineName)
- {
- if (!CheckMachineName(machineName))
- throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
+ /// <summary>
+ /// Creates a ServiceController object, based on machine and service name.
+ /// </summary>
+ /// <param name="name">Name of the service</param>
+ /// <param name="machineName">Name of the machine</param>
+ public ServiceController(string name, string machineName)
+ {
+ if (!CheckMachineName(machineName))
+ throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(name), name));
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(name), name));
- _machineName = machineName;
- _eitherName = name;
- _type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
- }
+ _machineName = machineName;
+ _eitherName = name;
+ _type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
+ }
- private ServiceController(string machineName, Interop.Advapi32.ENUM_SERVICE_STATUS status)
- {
- if (!CheckMachineName(machineName))
- throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
-
- _machineName = machineName;
- _name = status.serviceName;
- _displayName = status.displayName;
- _commandsAccepted = status.controlsAccepted;
- _status = (ServiceControllerStatus)status.currentState;
- _type = status.serviceType;
- _statusGenerated = true;
- }
+ private ServiceController(string machineName, Interop.Advapi32.ENUM_SERVICE_STATUS status)
+ {
+ if (!CheckMachineName(machineName))
+ throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
+
+ _machineName = machineName;
+ _name = status.serviceName;
+ _displayName = status.displayName;
+ _commandsAccepted = status.controlsAccepted;
+ _status = (ServiceControllerStatus)status.currentState;
+ _type = status.serviceType;
+ _statusGenerated = true;
+ }
- /// <summary>
- /// Used by the GetServicesInGroup method.
- /// </summary>
- /// <param name="machineName">Name of the machine</param>
- /// <param name="status">Service process status</param>
- private ServiceController(string machineName, Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS status)
- {
- if (!CheckMachineName(machineName))
- throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
-
- _machineName = machineName;
- _name = status.serviceName;
- _displayName = status.displayName;
- _commandsAccepted = status.controlsAccepted;
- _status = (ServiceControllerStatus)status.currentState;
- _type = status.serviceType;
- _statusGenerated = true;
- }
+ /// <summary>
+ /// Used by the GetServicesInGroup method.
+ /// </summary>
+ /// <param name="machineName">Name of the machine</param>
+ /// <param name="status">Service process status</param>
+ private ServiceController(string machineName, Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS status)
+ {
+ if (!CheckMachineName(machineName))
+ throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
+
+ _machineName = machineName;
+ _name = status.serviceName;
+ _displayName = status.displayName;
+ _commandsAccepted = status.controlsAccepted;
+ _status = (ServiceControllerStatus)status.currentState;
+ _type = status.serviceType;
+ _statusGenerated = true;
+ }
- /// <summary>
- /// Tells if the service referenced by this object can be paused.
- /// </summary>
- public bool CanPauseAndContinue
- {
- get
- {
- GenerateStatus();
- return (_commandsAccepted & Interop.Advapi32.AcceptOptions.ACCEPT_PAUSE_CONTINUE) != 0;
- }
- }
+ /// <summary>
+ /// Tells if the service referenced by this object can be paused.
+ /// </summary>
+ public bool CanPauseAndContinue
+ {
+ get
+ {
+ GenerateStatus();
+ return (_commandsAccepted & Interop.Advapi32.AcceptOptions.ACCEPT_PAUSE_CONTINUE) != 0;
+ }
+ }
- /// <summary>
- /// Tells if the service is notified when system shutdown occurs.
- /// </summary>
- public bool CanShutdown
- {
- get
- {
- GenerateStatus();
- return (_commandsAccepted & Interop.Advapi32.AcceptOptions.ACCEPT_SHUTDOWN) != 0;
- }
- }
+ /// <summary>
+ /// Tells if the service is notified when system shutdown occurs.
+ /// </summary>
+ public bool CanShutdown
+ {
+ get
+ {
+ GenerateStatus();
+ return (_commandsAccepted & Interop.Advapi32.AcceptOptions.ACCEPT_SHUTDOWN) != 0;
+ }
+ }
- /// <summary>
- /// Tells if the service referenced by this object can be stopped.
- /// </summary>
- public bool CanStop
- {
- get
- {
- GenerateStatus();
- return (_commandsAccepted & Interop.Advapi32.AcceptOptions.ACCEPT_STOP) != 0;
- }
- }
+ /// <summary>
+ /// Tells if the service referenced by this object can be stopped.
+ /// </summary>
+ public bool CanStop
+ {
+ get
+ {
+ GenerateStatus();
+ return (_commandsAccepted & Interop.Advapi32.AcceptOptions.ACCEPT_STOP) != 0;
+ }
+ }
- /// <summary>
- /// The descriptive name shown for this service in the Service applet.
- /// </summary>
- public string DisplayName
- {
- get
- {
- if (string.IsNullOrEmpty(_displayName))
- GenerateNames();
- return _displayName;
- }
- set
- {
- if (value == null)
- throw new ArgumentNullException(nameof(value));
-
- if (string.Equals(value, _displayName, StringComparison.OrdinalIgnoreCase))
- {
- // they're just changing the casing. No need to close.
- _displayName = value;
- return;
- }
-
- Close();
- _displayName = value;
- _name = "";
- }
- }
+ /// <summary>
+ /// The descriptive name shown for this service in the Service applet.
+ /// </summary>
+ public string DisplayName
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(_displayName))
+ GenerateNames();
+ return _displayName;
+ }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
- /// <summary>
- /// The set of services that depend on this service. These are the services that will be stopped if this service is stopped.
- /// </summary>
- public ServiceController[] DependentServices
- {
- get
- {
- if (_dependentServices == null)
- {
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_ENUMERATE_DEPENDENTS);
- // figure out how big a buffer we need to get the info
- int bytesNeeded = 0;
- int numEnumerated = 0;
- bool result = Interop.Advapi32.EnumDependentServices(serviceHandle, Interop.Advapi32.ServiceState.SERVICE_STATE_ALL, IntPtr.Zero, 0,
- ref bytesNeeded, ref numEnumerated);
- if (result)
- {
- _dependentServices = Array.Empty<ServiceController>();
- return _dependentServices;
- }
-
- int lastError = Marshal.GetLastWin32Error();
- if (lastError != Interop.Errors.ERROR_MORE_DATA)
- throw new Win32Exception(lastError);
-
- // allocate the buffer
- IntPtr enumBuffer = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
-
- try
- {
- // get all the info
- result = Interop.Advapi32.EnumDependentServices(serviceHandle, Interop.Advapi32.ServiceState.SERVICE_STATE_ALL, enumBuffer, bytesNeeded,
- ref bytesNeeded, ref numEnumerated);
- if (!result)
- throw new Win32Exception();
+ if (string.Equals(value, _displayName, StringComparison.OrdinalIgnoreCase))
+ {
+ // they're just changing the casing. No need to close.
+ _displayName = value;
+ return;
+ }
- // for each of the entries in the buffer, create a new ServiceController object.
- _dependentServices = new ServiceController[numEnumerated];
- for (int i = 0; i < numEnumerated; i++)
- {
- Interop.Advapi32.ENUM_SERVICE_STATUS status = new Interop.Advapi32.ENUM_SERVICE_STATUS();
- IntPtr structPtr = (IntPtr)((long)enumBuffer + (i * Marshal.SizeOf<Interop.Advapi32.ENUM_SERVICE_STATUS>()));
- Marshal.PtrToStructure(structPtr, status);
- _dependentServices[i] = new ServiceController(_machineName, status);
+ Close();
+ _displayName = value;
+ _name = "";
}
- }
- finally
- {
- Marshal.FreeHGlobal(enumBuffer);
- }
}
- return _dependentServices;
- }
- }
+ /// <summary>
+ /// The set of services that depend on this service. These are the services that will be stopped if this service is stopped.
+ /// </summary>
+ public ServiceController[] DependentServices
+ {
+ get
+ {
+ if (_dependentServices == null)
+ {
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_ENUMERATE_DEPENDENTS);
+ // figure out how big a buffer we need to get the info
+ int bytesNeeded = 0;
+ int numEnumerated = 0;
+ bool result = Interop.Advapi32.EnumDependentServices(serviceHandle, Interop.Advapi32.ServiceState.SERVICE_STATE_ALL, IntPtr.Zero, 0,
+ ref bytesNeeded, ref numEnumerated);
+ if (result)
+ {
+ _dependentServices = Array.Empty<ServiceController>();
+ return _dependentServices;
+ }
+
+ int lastError = Marshal.GetLastWin32Error();
+ if (lastError != Interop.Errors.ERROR_MORE_DATA)
+ throw new Win32Exception(lastError);
+
+ // allocate the buffer
+ IntPtr enumBuffer = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
+
+ try
+ {
+ // get all the info
+ result = Interop.Advapi32.EnumDependentServices(serviceHandle, Interop.Advapi32.ServiceState.SERVICE_STATE_ALL, enumBuffer, bytesNeeded,
+ ref bytesNeeded, ref numEnumerated);
+ if (!result)
+ throw new Win32Exception();
+
+ // for each of the entries in the buffer, create a new ServiceController object.
+ _dependentServices = new ServiceController[numEnumerated];
+ for (int i = 0; i < numEnumerated; i++)
+ {
+ Interop.Advapi32.ENUM_SERVICE_STATUS status = new Interop.Advapi32.ENUM_SERVICE_STATUS();
+ IntPtr structPtr = (IntPtr)((long)enumBuffer + (i * Marshal.SizeOf<Interop.Advapi32.ENUM_SERVICE_STATUS>()));
+ Marshal.PtrToStructure(structPtr, status);
+ _dependentServices[i] = new ServiceController(_machineName, status);
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(enumBuffer);
+ }
+ }
- /// <summary>
- /// The name of the machine on which this service resides.
- /// </summary>
- public string MachineName
- {
- get
- {
- return _machineName;
- }
- set
- {
- if (!CheckMachineName(value))
- throw new ArgumentException(SR.Format(SR.BadMachineName, value));
-
- if (string.Equals(_machineName, value, StringComparison.OrdinalIgnoreCase))
- {
- // no need to close, because the most they're changing is the
- // casing.
- _machineName = value;
- return;
- }
-
- Close();
- _machineName = value;
- }
- }
+ return _dependentServices;
+ }
+ }
- /// <summary>
- /// Returns the short name of the service referenced by this object.
- /// </summary>
- public string ServiceName
- {
- get
- {
- if (string.IsNullOrEmpty(_name))
- GenerateNames();
- return _name;
- }
- set
- {
- if (value == null)
- throw new ArgumentNullException(nameof(value));
-
- if (string.Equals(value, _name, StringComparison.OrdinalIgnoreCase))
- {
- // they might be changing the casing, but the service we refer to
- // is the same. No need to close.
- _name = value;
- return;
- }
-
- if (!ServiceBase.ValidServiceName(value))
- throw new ArgumentException(SR.Format(SR.ServiceName, value, ServiceBase.MaxNameLength.ToString()));
-
- Close();
- _name = value;
- _displayName = "";
- }
- }
+ /// <summary>
+ /// The name of the machine on which this service resides.
+ /// </summary>
+ public string MachineName
+ {
+ get
+ {
+ return _machineName;
+ }
+ set
+ {
+ if (!CheckMachineName(value))
+ throw new ArgumentException(SR.Format(SR.BadMachineName, value));
- /// <summary>
- /// A set of services on which the given service object is depend upon.
- /// </summary>
- public unsafe ServiceController[] ServicesDependedOn
- {
- get
- {
- if (_servicesDependedOn != null)
- return _servicesDependedOn;
-
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_QUERY_CONFIG);
- bool success = Interop.Advapi32.QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out int bytesNeeded);
- if (success)
- {
- _servicesDependedOn = Array.Empty<ServiceController>();
- return _servicesDependedOn;
- }
-
- int lastError = Marshal.GetLastWin32Error();
- if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
- throw new Win32Exception(lastError);
-
- // get the info
- IntPtr bufPtr = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
- try
- {
- success = Interop.Advapi32.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
- if (!success)
- throw new Win32Exception(Marshal.GetLastWin32Error());
-
- Interop.Advapi32.QUERY_SERVICE_CONFIG config = new Interop.Advapi32.QUERY_SERVICE_CONFIG();
- Marshal.PtrToStructure(bufPtr, config);
- Dictionary<string, ServiceController>? dependencyHash = null;
-
- char* dependencyChar = config.lpDependencies;
- if (dependencyChar != null)
- {
- // lpDependencies points to the start of multiple null-terminated strings. The list is
- // double-null terminated.
- int length = 0;
- dependencyHash = new Dictionary<string, ServiceController>();
- while (*(dependencyChar + length) != '\0')
- {
- length++;
- if (*(dependencyChar + length) == '\0')
- {
- string dependencyNameStr = new string(dependencyChar, 0, length);
- dependencyChar = dependencyChar + length + 1;
- length = 0;
- if (dependencyNameStr.StartsWith("+", StringComparison.Ordinal))
+ if (string.Equals(_machineName, value, StringComparison.OrdinalIgnoreCase))
{
- // this entry is actually a service load group
- Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS[] loadGroup = GetServicesInGroup(_machineName, dependencyNameStr.Substring(1));
- foreach (Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS groupMember in loadGroup)
- {
- if (!dependencyHash.ContainsKey(groupMember.serviceName!))
- dependencyHash.Add(groupMember.serviceName!, new ServiceController(MachineName, groupMember));
- }
+ // no need to close, because the most they're changing is the
+ // casing.
+ _machineName = value;
+ return;
}
- else
+
+ Close();
+ _machineName = value;
+ }
+ }
+
+ /// <summary>
+ /// Returns the short name of the service referenced by this object.
+ /// </summary>
+ public string ServiceName
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(_name))
+ GenerateNames();
+ return _name;
+ }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ if (string.Equals(value, _name, StringComparison.OrdinalIgnoreCase))
{
- if (!dependencyHash.ContainsKey(dependencyNameStr))
- dependencyHash.Add(dependencyNameStr, new ServiceController(dependencyNameStr, MachineName));
+ // they might be changing the casing, but the service we refer to
+ // is the same. No need to close.
+ _name = value;
+ return;
}
- }
+
+ if (!ServiceBase.ValidServiceName(value))
+ throw new ArgumentException(SR.Format(SR.ServiceName, value, ServiceBase.MaxNameLength.ToString()));
+
+ Close();
+ _name = value;
+ _displayName = "";
}
- }
+ }
+
+ /// <summary>
+ /// A set of services on which the given service object is depend upon.
+ /// </summary>
+ public unsafe ServiceController[] ServicesDependedOn
+ {
+ get
+ {
+ if (_servicesDependedOn != null)
+ return _servicesDependedOn;
+
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_QUERY_CONFIG);
+ bool success = Interop.Advapi32.QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out int bytesNeeded);
+ if (success)
+ {
+ _servicesDependedOn = Array.Empty<ServiceController>();
+ return _servicesDependedOn;
+ }
- if (dependencyHash != null)
- {
- _servicesDependedOn = new ServiceController[dependencyHash.Count];
- dependencyHash.Values.CopyTo(_servicesDependedOn, 0);
- }
- else
- {
- _servicesDependedOn = Array.Empty<ServiceController>();
- }
+ int lastError = Marshal.GetLastWin32Error();
+ if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+ throw new Win32Exception(lastError);
- return _servicesDependedOn;
+ // get the info
+ IntPtr bufPtr = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
+ try
+ {
+ success = Interop.Advapi32.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
+ if (!success)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ Interop.Advapi32.QUERY_SERVICE_CONFIG config = new Interop.Advapi32.QUERY_SERVICE_CONFIG();
+ Marshal.PtrToStructure(bufPtr, config);
+ Dictionary<string, ServiceController>? dependencyHash = null;
+
+ char* dependencyChar = config.lpDependencies;
+ if (dependencyChar != null)
+ {
+ // lpDependencies points to the start of multiple null-terminated strings. The list is
+ // double-null terminated.
+ int length = 0;
+ dependencyHash = new Dictionary<string, ServiceController>();
+ while (*(dependencyChar + length) != '\0')
+ {
+ length++;
+ if (*(dependencyChar + length) == '\0')
+ {
+ string dependencyNameStr = new string(dependencyChar, 0, length);
+ dependencyChar = dependencyChar + length + 1;
+ length = 0;
+ if (dependencyNameStr.StartsWith("+", StringComparison.Ordinal))
+ {
+ // this entry is actually a service load group
+ Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS[] loadGroup = GetServicesInGroup(_machineName, dependencyNameStr.Substring(1));
+ foreach (Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS groupMember in loadGroup)
+ {
+ if (!dependencyHash.ContainsKey(groupMember.serviceName!))
+ dependencyHash.Add(groupMember.serviceName!, new ServiceController(MachineName, groupMember));
+ }
+ }
+ else
+ {
+ if (!dependencyHash.ContainsKey(dependencyNameStr))
+ dependencyHash.Add(dependencyNameStr, new ServiceController(dependencyNameStr, MachineName));
+ }
+ }
+ }
+ }
+
+ if (dependencyHash != null)
+ {
+ _servicesDependedOn = new ServiceController[dependencyHash.Count];
+ dependencyHash.Values.CopyTo(_servicesDependedOn, 0);
+ }
+ else
+ {
+ _servicesDependedOn = Array.Empty<ServiceController>();
+ }
+
+ return _servicesDependedOn;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(bufPtr);
+ }
+ }
}
- finally
+
+ public ServiceStartMode StartType
{
- Marshal.FreeHGlobal(bufPtr);
+ get
+ {
+ if (_startTypeInitialized)
+ return _startType;
+
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_QUERY_CONFIG);
+ bool success = Interop.Advapi32.QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out int bytesNeeded);
+
+ int lastError = Marshal.GetLastWin32Error();
+ if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+ throw new Win32Exception(lastError);
+
+ // get the info
+ IntPtr bufPtr = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
+ try
+ {
+ success = Interop.Advapi32.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
+ if (!success)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ Interop.Advapi32.QUERY_SERVICE_CONFIG config = new Interop.Advapi32.QUERY_SERVICE_CONFIG();
+ Marshal.PtrToStructure(bufPtr, config);
+
+ _startType = (ServiceStartMode)config.dwStartType;
+ _startTypeInitialized = true;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(bufPtr);
+ }
+
+ return _startType;
+ }
}
- }
- }
- public ServiceStartMode StartType
- {
- get
- {
- if (_startTypeInitialized)
- return _startType;
+ public SafeHandle ServiceHandle
+ {
+ get
+ {
+ return GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_ALL_ACCESS);
+ }
+ }
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_QUERY_CONFIG);
- bool success = Interop.Advapi32.QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out int bytesNeeded);
+ /// <summary>
+ /// Gets the status of the service referenced by this object, e.g., Running, Stopped, etc.
+ /// </summary>
+ /// <remarks>
+ /// Please see <see cref="ServiceControllerStatus"/> for more available status.
+ /// </remarks>
+ public ServiceControllerStatus Status
+ {
+ get
+ {
+ GenerateStatus();
+ return _status;
+ }
+ }
- int lastError = Marshal.GetLastWin32Error();
- if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
- throw new Win32Exception(lastError);
+ /// <summary>
+ /// Gets the type of service that this object references.
+ /// </summary>
+ /// <remarks>
+ /// Please see <see cref="System.ServiceProcess.ServiceType"/> for available list of Service types.
+ /// </remarks>
+ public ServiceType ServiceType
+ {
+ get
+ {
+ GenerateStatus();
+ return (ServiceType)_type;
+ }
+ }
- // get the info
- IntPtr bufPtr = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
- try
+ private static bool CheckMachineName(string value)
{
- success = Interop.Advapi32.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
- if (!success)
- throw new Win32Exception(Marshal.GetLastWin32Error());
+ // string.Contains(char) is .NetCore2.1+ specific
+ return !string.IsNullOrWhiteSpace(value) && value.IndexOf('\\') == -1;
+ }
- Interop.Advapi32.QUERY_SERVICE_CONFIG config = new Interop.Advapi32.QUERY_SERVICE_CONFIG();
- Marshal.PtrToStructure(bufPtr, config);
+ /// <summary>
+ /// Closes the handle to the service manager, but does not
+ /// mark the class as disposed.
+ /// </summary>
+ /// <remarks>
+ /// Violates design guidelines by not matching Dispose() -- matches .NET Framework
+ /// </remarks>
+ public void Close()
+ {
+ if (_serviceManagerHandle != null)
+ {
+ _serviceManagerHandle.Dispose();
+ _serviceManagerHandle = null;
+ }
- _startType = (ServiceStartMode)config.dwStartType;
- _startTypeInitialized = true;
+ _statusGenerated = false;
+ _startTypeInitialized = false;
+ _type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
}
- finally
+
+ /// <summary>
+ /// Closes the handle to the service manager, and disposes.
+ /// </summary>
+ protected override void Dispose(bool disposing)
{
- Marshal.FreeHGlobal(bufPtr);
+ Close();
+ _disposed = true;
+ base.Dispose(disposing);
}
- return _startType;
- }
- }
+ private unsafe void GenerateStatus()
+ {
+ if (_statusGenerated)
+ {
+ return;
+ }
- public SafeHandle ServiceHandle
- {
- get
- {
- return GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_ALL_ACCESS);
- }
- }
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_QUERY_STATUS);
+ Interop.Advapi32.SERVICE_STATUS svcStatus = default;
+ bool success = Interop.Advapi32.QueryServiceStatus(serviceHandle, &svcStatus);
+ if (!success)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
- /// <summary>
- /// Gets the status of the service referenced by this object, e.g., Running, Stopped, etc.
- /// </summary>
- /// <remarks>
- /// Please see <see cref="ServiceControllerStatus"/> for more available status.
- /// </remarks>
- public ServiceControllerStatus Status
- {
- get
- {
- GenerateStatus();
- return _status;
- }
- }
+ _commandsAccepted = svcStatus.controlsAccepted;
+ _status = (ServiceControllerStatus)svcStatus.currentState;
+ _type = svcStatus.serviceType;
+ _statusGenerated = true;
+ }
- /// <summary>
- /// Gets the type of service that this object references.
- /// </summary>
- /// <remarks>
- /// Please see <see cref="System.ServiceProcess.ServiceType"/> for available list of Service types.
- /// </remarks>
- public ServiceType ServiceType
- {
- get
- {
- GenerateStatus();
- return (ServiceType)_type;
- }
- }
+ [MemberNotNull(nameof(_name))]
+ [MemberNotNull(nameof(_displayName))]
+ private void GenerateNames()
+ {
+ GetDataBaseHandleWithConnectAccess();
- private static bool CheckMachineName(string value)
- {
- // string.Contains(char) is .NetCore2.1+ specific
- return !string.IsNullOrWhiteSpace(value) && value.IndexOf('\\') == -1;
- }
+ if (string.IsNullOrEmpty(_name))
+ {
+ // Figure out the _name based on the information we have.
+ // We must either have _displayName or the constructor parameter _eitherName.
+ string? userGivenName = string.IsNullOrEmpty(_eitherName) ? _displayName : _eitherName;
- /// <summary>
- /// Closes the handle to the service manager, but does not
- /// mark the class as disposed.
- /// </summary>
- /// <remarks>
- /// Violates design guidelines by not matching Dispose() -- matches .NET Framework
- /// </remarks>
- public void Close()
- {
- if (_serviceManagerHandle != null)
- {
- _serviceManagerHandle.Dispose();
- _serviceManagerHandle = null;
- }
-
- _statusGenerated = false;
- _startTypeInitialized = false;
- _type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
- }
+ if (string.IsNullOrEmpty(userGivenName))
+ throw new InvalidOperationException(SR.Format(SR.ServiceName, userGivenName, ServiceBase.MaxNameLength.ToString()));
- /// <summary>
- /// Closes the handle to the service manager, and disposes.
- /// </summary>
- protected override void Dispose(bool disposing)
- {
- Close();
- _disposed = true;
- base.Dispose(disposing);
- }
+ // Try it as a display name
+ string? result = GetServiceKeyName(_serviceManagerHandle, userGivenName);
- private unsafe void GenerateStatus()
- {
- if (_statusGenerated)
- {
- return;
- }
-
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_QUERY_STATUS);
- Interop.Advapi32.SERVICE_STATUS svcStatus = default;
- bool success = Interop.Advapi32.QueryServiceStatus(serviceHandle, &svcStatus);
- if (!success)
- throw new Win32Exception(Marshal.GetLastWin32Error());
-
- _commandsAccepted = svcStatus.controlsAccepted;
- _status = (ServiceControllerStatus)svcStatus.currentState;
- _type = svcStatus.serviceType;
- _statusGenerated = true;
- }
+ if (result != null)
+ {
+ // Now we have both
+ _name = result;
+ _displayName = userGivenName;
+ _eitherName = null;
+ return;
+ }
- [MemberNotNull(nameof(_name))]
- [MemberNotNull(nameof(_displayName))]
- private void GenerateNames()
- {
- GetDataBaseHandleWithConnectAccess();
+ // Try it as a service name
+ result = GetServiceDisplayName(_serviceManagerHandle, userGivenName);
- if (string.IsNullOrEmpty(_name))
- {
- // Figure out the _name based on the information we have.
- // We must either have _displayName or the constructor parameter _eitherName.
- string? userGivenName = string.IsNullOrEmpty(_eitherName) ? _displayName : _eitherName;
+ if (result == null)
+ {
+ throw new InvalidOperationException(SR.Format(SR.NoService, userGivenName, _machineName), new Win32Exception(Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST));
+ }
- if (string.IsNullOrEmpty(userGivenName))
- throw new InvalidOperationException(SR.Format(SR.ServiceName, userGivenName, ServiceBase.MaxNameLength.ToString()));
+ _name = userGivenName;
+ _displayName = result;
+ _eitherName = null;
+ }
+ else if (string.IsNullOrEmpty(_displayName))
+ {
+ // We must have _name
+ string? result = GetServiceDisplayName(_serviceManagerHandle, _name);
- // Try it as a display name
- string? result = GetServiceKeyName(_serviceManagerHandle, userGivenName);
+ if (result == null)
+ {
+ throw new InvalidOperationException(SR.Format(SR.NoService, _name, _machineName), new Win32Exception(Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST));
+ }
- if (result != null)
- {
- // Now we have both
- _name = result;
- _displayName = userGivenName;
- _eitherName = null;
- return;
+ _displayName = result;
+ _eitherName = null;
+ }
}
- // Try it as a service name
- result = GetServiceDisplayName(_serviceManagerHandle, userGivenName);
-
- if (result == null)
+ /// <summary>
+ /// Gets service name (key name) from service display name.
+ /// Returns null if service is not found.
+ /// </summary>
+ private unsafe string? GetServiceKeyName(SafeServiceHandle? SCMHandle, string serviceDisplayName)
{
- throw new InvalidOperationException(SR.Format(SR.NoService, userGivenName, _machineName), new Win32Exception(Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST));
- }
+ var builder = new ValueStringBuilder(stackalloc char[256]);
+ int bufLen;
+ while (true)
+ {
+ bufLen = builder.Capacity;
+ fixed (char* c = builder)
+ {
+ if (Interop.Advapi32.GetServiceKeyName(SCMHandle, serviceDisplayName, c, ref bufLen))
+ break;
+ }
- _name = userGivenName;
- _displayName = result;
- _eitherName = null;
- }
- else if (string.IsNullOrEmpty(_displayName))
- {
- // We must have _name
- string? result = GetServiceDisplayName(_serviceManagerHandle, _name);
+ int lastError = Marshal.GetLastWin32Error();
+ if (lastError == Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ return null;
+ }
- if (result == null)
- {
- throw new InvalidOperationException(SR.Format(SR.NoService, _name, _machineName), new Win32Exception(Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST));
- }
+ if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+ {
+ throw new InvalidOperationException(SR.Format(SR.NoService, serviceDisplayName, _machineName), new Win32Exception(lastError));
+ }
- _displayName = result;
- _eitherName = null;
- }
- }
+ builder.EnsureCapacity(bufLen + 1); // Does not include null
+ }
- /// <summary>
- /// Gets service name (key name) from service display name.
- /// Returns null if service is not found.
- /// </summary>
- private unsafe string? GetServiceKeyName(SafeServiceHandle? SCMHandle, string serviceDisplayName)
- {
- var builder = new ValueStringBuilder(stackalloc char[256]);
- int bufLen;
- while (true)
- {
- bufLen = builder.Capacity;
- fixed (char* c = builder)
- {
- if (Interop.Advapi32.GetServiceKeyName(SCMHandle, serviceDisplayName, c, ref bufLen))
- break;
+ builder.Length = bufLen;
+ return builder.ToString();
}
- int lastError = Marshal.GetLastWin32Error();
- if (lastError == Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST)
+ private unsafe string? GetServiceDisplayName(SafeServiceHandle? scmHandle, string serviceName)
{
- return null;
+ var builder = new ValueStringBuilder(4096);
+ int bufLen;
+ while (true)
+ {
+ bufLen = builder.Capacity;
+ fixed (char* c = builder)
+ {
+ if (Interop.Advapi32.GetServiceDisplayName(scmHandle, serviceName, c, ref bufLen))
+ break;
+ }
+
+ int lastError = Marshal.GetLastWin32Error();
+ if (lastError == Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ return null;
+ }
+ else if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+ {
+ throw new InvalidOperationException(SR.Format(SR.NoService, serviceName, _machineName), new Win32Exception(lastError));
+ }
+
+ builder.EnsureCapacity(bufLen + 1); // Does not include null
+ }
+
+ builder.Length = bufLen;
+ return builder.ToString();
}
- if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+ private static SafeServiceHandle GetDataBaseHandleWithAccess(string machineName, int serviceControlManagerAccess)
{
- throw new InvalidOperationException(SR.Format(SR.NoService, serviceDisplayName, _machineName), new Win32Exception(lastError));
- }
+ SafeServiceHandle? databaseHandle;
+ if (machineName.Equals(DefaultMachineName) || machineName.Length == 0)
+ {
+ databaseHandle = new SafeServiceHandle(Interop.Advapi32.OpenSCManager(null, null, serviceControlManagerAccess));
+ }
+ else
+ {
+ databaseHandle = new SafeServiceHandle(Interop.Advapi32.OpenSCManager(machineName, null, serviceControlManagerAccess));
+ }
- builder.EnsureCapacity(bufLen + 1); // Does not include null
- }
+ if (databaseHandle.IsInvalid)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.OpenSC, machineName), inner);
+ }
- builder.Length = bufLen;
- return builder.ToString();
- }
+ return databaseHandle;
+ }
- private unsafe string? GetServiceDisplayName(SafeServiceHandle? scmHandle, string serviceName)
- {
- var builder = new ValueStringBuilder(4096);
- int bufLen;
- while (true)
- {
- bufLen = builder.Capacity;
- fixed (char* c = builder)
+ private void GetDataBaseHandleWithConnectAccess()
{
- if (Interop.Advapi32.GetServiceDisplayName(scmHandle, serviceName, c, ref bufLen))
- break;
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+
+ // get a handle to SCM with connect access and store it in serviceManagerHandle field.
+ if (_serviceManagerHandle == null)
+ {
+ _serviceManagerHandle = GetDataBaseHandleWithAccess(_machineName, Interop.Advapi32.ServiceControllerOptions.SC_MANAGER_CONNECT);
+ }
}
- int lastError = Marshal.GetLastWin32Error();
- if (lastError == Interop.Errors.ERROR_SERVICE_DOES_NOT_EXIST)
+ /// <summary>
+ /// Gets all the device-driver services with <see cref="DefaultMachineName"/>.
+ /// </summary>
+ /// <returns>Set of service controllers</returns>
+ public static ServiceController[] GetDevices()
{
- return null;
+ return GetDevices(DefaultMachineName);
}
- else if (lastError != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+
+ /// <summary>
+ /// Gets all the device-driver services in the machine specified.
+ /// </summary>
+ /// <param name="machineName">Name of the machine.</param>
+ /// <returns>Set of service controllers</returns>
+ public static ServiceController[] GetDevices(string machineName)
{
- throw new InvalidOperationException(SR.Format(SR.NoService, serviceName, _machineName), new Win32Exception(lastError));
+ return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_DRIVER);
}
- builder.EnsureCapacity(bufLen + 1); // Does not include null
- }
+ /// <summary>
+ /// Opens a handle for the current service. The handle must be Dispose()'d.
+ /// </summary>
+ /// <param name="desiredAccess">Access level to pass to OpenService()</param>
+ /// <returns></returns>
+ private SafeServiceHandle GetServiceHandle(int desiredAccess)
+ {
+ GetDataBaseHandleWithConnectAccess();
- builder.Length = bufLen;
- return builder.ToString();
- }
+ var serviceHandle = new SafeServiceHandle(Interop.Advapi32.OpenService(_serviceManagerHandle, ServiceName, desiredAccess));
+ if (serviceHandle.IsInvalid)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.OpenService, ServiceName, _machineName), inner);
+ }
- private static SafeServiceHandle GetDataBaseHandleWithAccess(string machineName, int serviceControlManagerAccess)
- {
- SafeServiceHandle? databaseHandle;
- if (machineName.Equals(DefaultMachineName) || machineName.Length == 0)
- {
- databaseHandle = new SafeServiceHandle(Interop.Advapi32.OpenSCManager(null, null, serviceControlManagerAccess));
- }
- else
- {
- databaseHandle = new SafeServiceHandle(Interop.Advapi32.OpenSCManager(machineName, null, serviceControlManagerAccess));
- }
-
- if (databaseHandle.IsInvalid)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.OpenSC, machineName), inner);
- }
-
- return databaseHandle;
- }
+ return serviceHandle;
+ }
- private void GetDataBaseHandleWithConnectAccess()
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- // get a handle to SCM with connect access and store it in serviceManagerHandle field.
- if (_serviceManagerHandle == null)
- {
- _serviceManagerHandle = GetDataBaseHandleWithAccess(_machineName, Interop.Advapi32.ServiceControllerOptions.SC_MANAGER_CONNECT);
- }
- }
+ /// <summary>
+ /// Gets the services (not including device-driver services) on the local machine.
+ /// </summary>
+ /// <returns>Set of service controllers</returns>
+ public static ServiceController[] GetServices()
+ {
+ return GetServices(DefaultMachineName);
+ }
- /// <summary>
- /// Gets all the device-driver services with <see cref="DefaultMachineName"/>.
- /// </summary>
- /// <returns>Set of service controllers</returns>
- public static ServiceController[] GetDevices()
- {
- return GetDevices(DefaultMachineName);
- }
+ /// <summary>
+ /// Gets the services (not including device-driver services) on the given machine name.
+ /// /// </summary>
+ /// <param name="machineName">Name of the machine</param>
+ /// <returns></returns>
+ public static ServiceController[] GetServices(string machineName)
+ {
+ return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32);
+ }
- /// <summary>
- /// Gets all the device-driver services in the machine specified.
- /// </summary>
- /// <param name="machineName">Name of the machine.</param>
- /// <returns>Set of service controllers</returns>
- public static ServiceController[] GetDevices(string machineName)
- {
- return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_DRIVER);
- }
+ /// <summary>
+ /// Helper function for ServicesDependedOn.
+ /// </summary>
+ /// <param name="machineName">Name of the machine.</param>
+ /// <param name="group">Name of the group.</param>
+ /// <returns></returns>
+ private static Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS[] GetServicesInGroup(string machineName, string group)
+ {
+ return GetServices(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32, group, status => status);
+ }
- /// <summary>
- /// Opens a handle for the current service. The handle must be Dispose()'d.
- /// </summary>
- /// <param name="desiredAccess">Access level to pass to OpenService()</param>
- /// <returns></returns>
- private SafeServiceHandle GetServiceHandle(int desiredAccess)
- {
- GetDataBaseHandleWithConnectAccess();
+ /// <summary>
+ /// Helper function for GetDevices and GetServices.
+ /// </summary>
+ /// <param name="machineName">Name of the machine.</param>
+ /// <param name="serviceType">Type of service.</param>
+ /// <returns></returns>
+ private static ServiceController[] GetServicesOfType(string machineName, int serviceType)
+ {
+ if (!CheckMachineName(machineName))
+ throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
- var serviceHandle = new SafeServiceHandle(Interop.Advapi32.OpenService(_serviceManagerHandle, ServiceName, desiredAccess));
- if (serviceHandle.IsInvalid)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.OpenService, ServiceName, _machineName), inner);
- }
+ return GetServices(machineName, serviceType, null, status => new ServiceController(machineName, status));
+ }
- return serviceHandle;
- }
+ /// Helper for GetDevices, GetServices, and ServicesDependedOn
+ private static T[] GetServices<T>(string machineName, int serviceType, string? group, Func<Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS, T> selector)
+ {
+ int resumeHandle = 0;
+
+ T[] services;
+
+ using SafeServiceHandle databaseHandle = GetDataBaseHandleWithAccess(machineName, Interop.Advapi32.ServiceControllerOptions.SC_MANAGER_ENUMERATE_SERVICE);
+ Interop.Advapi32.EnumServicesStatusEx(
+ databaseHandle,
+ Interop.Advapi32.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
+ serviceType,
+ Interop.Advapi32.StatusOptions.STATUS_ALL,
+ IntPtr.Zero,
+ 0,
+ out int bytesNeeded,
+ out int servicesReturned,
+ ref resumeHandle,
+ group);
+
+ IntPtr memory = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
+ try
+ {
+ //
+ // Get the set of services
+ //
+ Interop.Advapi32.EnumServicesStatusEx(
+ databaseHandle,
+ Interop.Advapi32.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
+ serviceType,
+ Interop.Advapi32.StatusOptions.STATUS_ALL,
+ memory,
+ bytesNeeded,
+ out bytesNeeded,
+ out servicesReturned,
+ ref resumeHandle,
+ group);
+
+ //
+ // Go through the block of memory it returned to us and select the results
+ //
+ services = new T[servicesReturned];
+ for (int i = 0; i < servicesReturned; i++)
+ {
+ IntPtr structPtr = (IntPtr)((long)memory + (i * Marshal.SizeOf<Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS>()));
+ Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS status = new Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS();
+ Marshal.PtrToStructure(structPtr, status);
+ services[i] = selector(status);
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(memory);
+ }
- /// <summary>
- /// Gets the services (not including device-driver services) on the local machine.
- /// </summary>
- /// <returns>Set of service controllers</returns>
- public static ServiceController[] GetServices()
- {
- return GetServices(DefaultMachineName);
- }
+ return services;
+ }
- /// <summary>
- /// Gets the services (not including device-driver services) on the given machine name.
- /// /// </summary>
- /// <param name="machineName">Name of the machine</param>
- /// <returns></returns>
- public static ServiceController[] GetServices(string machineName)
- {
- return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32);
- }
+ /// <summary>
+ /// Suspends a service's operation.
+ /// </summary>
+ public unsafe void Pause()
+ {
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_PAUSE_CONTINUE);
+ Interop.Advapi32.SERVICE_STATUS status = default;
+ bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_PAUSE, &status);
- /// <summary>
- /// Helper function for ServicesDependedOn.
- /// </summary>
- /// <param name="machineName">Name of the machine.</param>
- /// <param name="group">Name of the group.</param>
- /// <returns></returns>
- private static Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS[] GetServicesInGroup(string machineName, string group)
- {
- return GetServices(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32, group, status => status);
- }
+ if (!result)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.PauseService, ServiceName, _machineName), inner);
+ }
+ }
- /// <summary>
- /// Helper function for GetDevices and GetServices.
- /// </summary>
- /// <param name="machineName">Name of the machine.</param>
- /// <param name="serviceType">Type of service.</param>
- /// <returns></returns>
- private static ServiceController[] GetServicesOfType(string machineName, int serviceType)
- {
- if (!CheckMachineName(machineName))
- throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
+ /// <summary>
+ /// Continues a service after it has been paused.
+ /// </summary>
+ public unsafe void Continue()
+ {
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_PAUSE_CONTINUE);
+ Interop.Advapi32.SERVICE_STATUS status = default;
+ bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_CONTINUE, &status);
+ if (!result)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.ResumeService, ServiceName, _machineName), inner);
+ }
+ }
- return GetServices(machineName, serviceType, null, status => new ServiceController(machineName, status));
- }
+ /// <summary>
+ /// Executes the command.
+ /// </summary>
+ /// <param name="command">The command</param>
+ public unsafe void ExecuteCommand(int command)
+ {
+ using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_USER_DEFINED_CONTROL);
+ Interop.Advapi32.SERVICE_STATUS status = default;
+ bool result = Interop.Advapi32.ControlService(serviceHandle, command, &status);
+ if (!result)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.ControlService, ServiceName, MachineName), inner);
+ }
+ }
- /// Helper for GetDevices, GetServices, and ServicesDependedOn
- private static T[] GetServices<T>(string machineName, int serviceType, string? group, Func<Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS, T> selector)
- {
- int resumeHandle = 0;
-
- T[] services;
-
- using SafeServiceHandle databaseHandle = GetDataBaseHandleWithAccess(machineName, Interop.Advapi32.ServiceControllerOptions.SC_MANAGER_ENUMERATE_SERVICE);
- Interop.Advapi32.EnumServicesStatusEx(
- databaseHandle,
- Interop.Advapi32.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
- serviceType,
- Interop.Advapi32.StatusOptions.STATUS_ALL,
- IntPtr.Zero,
- 0,
- out int bytesNeeded,
- out int servicesReturned,
- ref resumeHandle,
- group);
-
- IntPtr memory = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
- try
- {
- //
- // Get the set of services
- //
- Interop.Advapi32.EnumServicesStatusEx(
- databaseHandle,
- Interop.Advapi32.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
- serviceType,
- Interop.Advapi32.StatusOptions.STATUS_ALL,
- memory,
- bytesNeeded,
- out bytesNeeded,
- out servicesReturned,
- ref resumeHandle,
- group);
-
- //
- // Go through the block of memory it returned to us and select the results
- //
- services = new T[servicesReturned];
- for (int i = 0; i < servicesReturned; i++)
- {
- IntPtr structPtr = (IntPtr)((long)memory + (i * Marshal.SizeOf<Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS>()));
- Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS status = new Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS();
- Marshal.PtrToStructure(structPtr, status);
- services[i] = selector(status);
- }
- }
- finally
- {
- Marshal.FreeHGlobal(memory);
- }
-
- return services;
- }
+ /// <summary>
+ /// Refreshes all property values.
+ /// </summary>
+ public void Refresh()
+ {
+ _statusGenerated = false;
+ _startTypeInitialized = false;
+ _dependentServices = null;
+ _servicesDependedOn = null;
+ }
- /// <summary>
- /// Suspends a service's operation.
- /// </summary>
- public unsafe void Pause()
- {
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_PAUSE_CONTINUE);
- Interop.Advapi32.SERVICE_STATUS status = default;
- bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_PAUSE, &status);
-
- if (!result)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.PauseService, ServiceName, _machineName), inner);
- }
- }
+ /// <summary>
+ /// Starts the service.
+ /// </summary>
+ public void Start()
+ {
+ Start(Array.Empty<string>());
+ }
- /// <summary>
- /// Continues a service after it has been paused.
- /// </summary>
- public unsafe void Continue()
- {
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_PAUSE_CONTINUE);
- Interop.Advapi32.SERVICE_STATUS status = default;
- bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_CONTINUE, &status);
- if (!result)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.ResumeService, ServiceName, _machineName), inner);
- }
- }
+ /// <summary>
+ /// Starts a service in the machine specified.
+ /// </summary>
+ public void Start(string[] args)
+ {
+ if (args == null)
+ throw new ArgumentNullException(nameof(args));
- /// <summary>
- /// Executes the command.
- /// </summary>
- /// <param name="command">The command</param>
- public unsafe void ExecuteCommand(int command)
- {
- using var serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_USER_DEFINED_CONTROL);
- Interop.Advapi32.SERVICE_STATUS status = default;
- bool result = Interop.Advapi32.ControlService(serviceHandle, command, &status);
- if (!result)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.ControlService, ServiceName, MachineName), inner);
- }
- }
+ using SafeServiceHandle serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_START);
+ IntPtr[] argPtrs = new IntPtr[args.Length];
+ int i = 0;
+ try
+ {
+ for (i = 0; i < args.Length; i++)
+ {
+ if (args[i] == null)
+ throw new ArgumentNullException($"{nameof(args)}[{i}]", SR.ArgsCantBeNull);
- /// <summary>
- /// Refreshes all property values.
- /// </summary>
- public void Refresh()
- {
- _statusGenerated = false;
- _startTypeInitialized = false;
- _dependentServices = null;
- _servicesDependedOn = null;
- }
+ argPtrs[i] = Marshal.StringToHGlobalUni(args[i]);
+ }
+ }
+ catch
+ {
+ for (int j = 0; j < i; j++)
+ Marshal.FreeHGlobal(argPtrs[i]);
+ throw;
+ }
- /// <summary>
- /// Starts the service.
- /// </summary>
- public void Start()
- {
- Start(Array.Empty<string>());
- }
+ GCHandle argPtrsHandle = default;
+ try
+ {
+ argPtrsHandle = GCHandle.Alloc(argPtrs, GCHandleType.Pinned);
+ bool result = Interop.Advapi32.StartService(serviceHandle, args.Length, argPtrsHandle.AddrOfPinnedObject());
+ if (!result)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.CannotStart, ServiceName, _machineName), inner);
+ }
+ }
+ finally
+ {
+ for (i = 0; i < args.Length; i++)
+ Marshal.FreeHGlobal(argPtrs[i]);
+ if (argPtrsHandle.IsAllocated)
+ argPtrsHandle.Free();
+ }
+ }
- /// <summary>
- /// Starts a service in the machine specified.
- /// </summary>
- public void Start(string[] args)
- {
- if (args == null)
- throw new ArgumentNullException(nameof(args));
-
- using SafeServiceHandle serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_START);
- IntPtr[] argPtrs = new IntPtr[args.Length];
- int i = 0;
- try
- {
- for (i = 0; i < args.Length; i++)
- {
- if (args[i] == null)
- throw new ArgumentNullException($"{nameof(args)}[{i}]", SR.ArgsCantBeNull);
-
- argPtrs[i] = Marshal.StringToHGlobalUni(args[i]);
- }
- }
- catch
- {
- for (int j = 0; j < i; j++)
- Marshal.FreeHGlobal(argPtrs[i]);
- throw;
- }
-
- GCHandle argPtrsHandle = default;
- try
- {
- argPtrsHandle = GCHandle.Alloc(argPtrs, GCHandleType.Pinned);
- bool result = Interop.Advapi32.StartService(serviceHandle, args.Length, argPtrsHandle.AddrOfPinnedObject());
- if (!result)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.CannotStart, ServiceName, _machineName), inner);
- }
- }
- finally
- {
- for (i = 0; i < args.Length; i++)
- Marshal.FreeHGlobal(argPtrs[i]);
- if (argPtrsHandle.IsAllocated)
- argPtrsHandle.Free();
- }
- }
+ /// <summary>
+ /// Stops the service. If any other services depend on this one for operation,
+ /// they will be stopped first. The DependentServices property lists this set
+ /// of services.
+ /// </summary>
+ public unsafe void Stop()
+ {
+ using SafeServiceHandle serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_STOP);
+ // Before stopping this service, stop all the dependent services that are running.
+ // (It's OK not to cache the result of getting the DependentServices property because it caches on its own.)
+ for (int i = 0; i < DependentServices.Length; i++)
+ {
+ ServiceController currentDependent = DependentServices[i];
+ currentDependent.Refresh();
+ if (currentDependent.Status != ServiceControllerStatus.Stopped)
+ {
+ currentDependent.Stop();
+ currentDependent.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 30));
+ }
+ }
- /// <summary>
- /// Stops the service. If any other services depend on this one for operation,
- /// they will be stopped first. The DependentServices property lists this set
- /// of services.
- /// </summary>
- public unsafe void Stop()
- {
- using SafeServiceHandle serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_STOP);
- // Before stopping this service, stop all the dependent services that are running.
- // (It's OK not to cache the result of getting the DependentServices property because it caches on its own.)
- for (int i = 0; i < DependentServices.Length; i++)
- {
- ServiceController currentDependent = DependentServices[i];
- currentDependent.Refresh();
- if (currentDependent.Status != ServiceControllerStatus.Stopped)
- {
- currentDependent.Stop();
- currentDependent.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 30));
- }
- }
-
- Interop.Advapi32.SERVICE_STATUS status = default;
- bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_STOP, &status);
- if (!result)
- {
- Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
- throw new InvalidOperationException(SR.Format(SR.StopService, ServiceName, _machineName), inner);
- }
- }
+ Interop.Advapi32.SERVICE_STATUS status = default;
+ bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_STOP, &status);
+ if (!result)
+ {
+ Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
+ throw new InvalidOperationException(SR.Format(SR.StopService, ServiceName, _machineName), inner);
+ }
+ }
- /// <summary>
- /// Waits infinitely until the service has reached the given status.
- /// </summary>
- /// <param name="desiredStatus">The status for which to wait.</param>
- public void WaitForStatus(ServiceControllerStatus desiredStatus)
- {
- WaitForStatus(desiredStatus, TimeSpan.MaxValue);
- }
+ /// <summary>
+ /// Waits infinitely until the service has reached the given status.
+ /// </summary>
+ /// <param name="desiredStatus">The status for which to wait.</param>
+ public void WaitForStatus(ServiceControllerStatus desiredStatus)
+ {
+ WaitForStatus(desiredStatus, TimeSpan.MaxValue);
+ }
- /// <summary>
- /// Waits until the service has reached the given status or until the specified time has expired.
- /// </summary>
- /// <param name="desiredStatus">The status for which to wait.</param>
- /// <param name="timeout">Wait for specific timeout</param>
- public void WaitForStatus(ServiceControllerStatus desiredStatus, TimeSpan timeout)
- {
- if (!Enum.IsDefined(typeof(ServiceControllerStatus), desiredStatus))
- throw new ArgumentException(SR.Format(SR.InvalidEnumArgument, nameof(desiredStatus), (int)desiredStatus, typeof(ServiceControllerStatus)));
-
- DateTime start = DateTime.UtcNow;
- Refresh();
- while (Status != desiredStatus)
- {
- if (DateTime.UtcNow - start > timeout)
- throw new System.ServiceProcess.TimeoutException(SR.Format(SR.Timeout, ServiceName));
-
- Thread.Sleep(250);
- Refresh();
- }
+ /// <summary>
+ /// Waits until the service has reached the given status or until the specified time has expired.
+ /// </summary>
+ /// <param name="desiredStatus">The status for which to wait.</param>
+ /// <param name="timeout">Wait for specific timeout</param>
+ public void WaitForStatus(ServiceControllerStatus desiredStatus, TimeSpan timeout)
+ {
+ if (!Enum.IsDefined(typeof(ServiceControllerStatus), desiredStatus))
+ throw new ArgumentException(SR.Format(SR.InvalidEnumArgument, nameof(desiredStatus), (int)desiredStatus, typeof(ServiceControllerStatus)));
+
+ DateTime start = DateTime.UtcNow;
+ Refresh();
+ while (Status != desiredStatus)
+ {
+ if (DateTime.UtcNow - start > timeout)
+ throw new System.ServiceProcess.TimeoutException(SR.Format(SR.Timeout, ServiceName));
+
+ Thread.Sleep(250);
+ Refresh();
+ }
+ }
}
- }
}