//
// System.ServiceProcess.ServiceController
//
-// Author: Marek Safar (marek.safar@seznam.cz)
+// Authors:
+// Marek Safar (marek.safar@seznam.cz)
+// Gert Driesen (drieseng@users.sourceforge.net)
//
// (C) 2005, Marek Safar
-
+//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+// TODO: check if there's more information to cache (eg. status)
+// Start / Stop / ...
using System;
using System.ComponentModel;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.ServiceProcess.Design;
+using System.Threading;
namespace System.ServiceProcess
{
+ [Designer("System.ServiceProcess.Design.ServiceControllerDesigner, " + Consts.AssemblySystem_Design)]
+ [MonoTODO ("No unix implementation")]
+ [ServiceProcessDescription ("Provides the ability to connect to, query, and manipulate running or stopped Windows services.")]
public class ServiceController : Component
{
+ private string _name;
+ private string _serviceName = string.Empty;
+ private string _machineName;
+ private string _displayName = string.Empty;
+ private readonly ServiceControllerImpl _impl;
+ private ServiceController [] _dependentServices;
+ private ServiceController [] _servicesDependedOn;
- [MonoTODO]
- public ServiceControllerStatus Status {
+ public ServiceController ()
+ {
+ _machineName = ".";
+ _name = string.Empty;
+ _impl = CreateServiceControllerImpl (this);
+ }
+
+ public ServiceController (string name) : this (name, ".")
+ {
+ }
+
+ public ServiceController (string name, string machineName)
+ {
+ if (name == null || name.Length == 0)
+ throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
+ "Invalid value {0} for parameter name.", name));
+
+ ValidateMachineName (machineName);
+
+ _machineName = machineName;
+ _name = name;
+ _impl = CreateServiceControllerImpl (this);
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("Whether this service recognizes the Pause and Continue commands.")]
+ public bool CanPauseAndContinue {
get {
- throw new NotImplementedException ();
+ ValidateServiceName (ServiceName);
+ return _impl.CanPauseAndContinue;
}
}
- [MonoTODO]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("Whether this service can respond to a system shutdown.")]
+ public bool CanShutdown {
+ get
+ {
+ ValidateServiceName (ServiceName);
+ return _impl.CanShutdown;
+ }
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("Whether this service can be stopped.")]
+ public bool CanStop {
+ get
+ {
+ ValidateServiceName (ServiceName);
+ return _impl.CanStop;
+ }
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("The services that depend on this service in order to run.")]
+ public ServiceController [] DependentServices {
+ get
+ {
+ ValidateServiceName (ServiceName);
+ if (_dependentServices == null)
+ _dependentServices = _impl.DependentServices;
+ return _dependentServices;
+ }
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ReadOnly (true)]
+ [ServiceProcessDescription ("The descriptive name of the service.")]
+ public string DisplayName {
+ get {
+ if (_displayName.Length == 0 && (_serviceName.Length > 0 || _name.Length > 0))
+ _displayName = _impl.DisplayName;
+ return _displayName;
+ }
+ set {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (_displayName == value)
+ return;
+
+ _displayName = value;
+
+ // if display name is modified, then we also need to force a
+ // new lookup of the corresponding service name
+ _serviceName = string.Empty;
+
+ // you'd expect the DependentServices and ServiceDependedOn cache
+ // to be cleared too, but the MS implementation doesn't do this
+ //
+ // categorized as by design:
+ // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
+
+ // release any handles and clear cache
+ Close ();
+ }
+ }
+
+ [Browsable (false)]
+ [DefaultValue (".")]
+ [RecommendedAsConfigurable (true)]
+ [ServiceProcessDescription ("The name of the machine on which this service resides.")]
+ public string MachineName {
+ get {
+ return _machineName;
+ }
+ set {
+ ValidateMachineName (value);
+
+ if (_machineName == value)
+ return;
+
+ _machineName = value;
+
+ // you'd expect the DependentServices and ServiceDependedOn cache
+ // to be cleared too, but the MS implementation doesn't do this
+ //
+ // categorized as by design:
+ // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
+
+ // release any handles and clear cache
+ Close ();
+ }
+ }
+
+ [DefaultValue ("")]
+ [ReadOnly (true)]
+ [RecommendedAsConfigurable (true)]
+ [ServiceProcessDescription ("The short name of the service.")]
+ [TypeConverter (typeof (ServiceNameConverter))]
public string ServiceName {
- get {
- throw new NotImplementedException ();
+ get {
+ if (_serviceName.Length == 0 && (_displayName.Length > 0 || _name.Length > 0))
+ _serviceName = _impl.ServiceName;
+ return _serviceName;
}
- set {
- throw new NotImplementedException ();
+ set {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (_serviceName == value)
+ return;
+
+ ValidateServiceName (value);
+
+ _serviceName = value;
+
+ // if service name is modified, then we also need to force a
+ // new lookup of the corresponding display name
+ _displayName = string.Empty;
+
+ // you'd expect the DependentServices and ServiceDependedOn cache
+ // to be cleared too, but the MS implementation doesn't do this
+ //
+ // categorized as by design:
+ // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
+
+ // release any handles and clear cache
+ Close ();
+ }
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("Services that must be started in order for this one to start.")]
+ public ServiceController [] ServicesDependedOn {
+ get
+ {
+ ValidateServiceName (ServiceName);
+ if (_servicesDependedOn == null)
+ _servicesDependedOn = _impl.ServicesDependedOn;
+ return _servicesDependedOn;
}
}
[MonoTODO]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public SafeHandle ServiceHandle
+ {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("The type of this service.")]
+ public ServiceType ServiceType {
+ get
+ {
+ ValidateServiceName (ServiceName);
+ return _impl.ServiceType;
+ }
+ }
+
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [ServiceProcessDescription ("The status of the service, e.g., Running or Stopped.")]
+ public ServiceControllerStatus Status {
+ get
+ {
+ ValidateServiceName (ServiceName);
+ return _impl.Status;
+ }
+ }
+
+ public void Close ()
+ {
+ _impl.Close ();
+ }
+
+ public void Continue ()
+ {
+ ValidateServiceName (ServiceName);
+ _impl.Continue ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ _impl.Dispose (disposing);
+ base.Dispose (disposing);
+ }
+
+ public void ExecuteCommand (int command)
+ {
+ ValidateServiceName (ServiceName);
+ _impl.ExecuteCommand (command);
+ }
+
+ public static ServiceController[] GetDevices ()
+ {
+ return GetDevices (".");
+ }
+
+ public static ServiceController[] GetDevices (string machineName)
+ {
+ ValidateMachineName (machineName);
+
+ using (ServiceController sc = new ServiceController ("dummy", machineName)) {
+ ServiceControllerImpl impl = CreateServiceControllerImpl (sc);
+ return impl.GetDevices ();
+ }
+ }
+
public static ServiceController[] GetServices ()
{
- throw new NotImplementedException ();
+ return GetServices (".");
}
+ public static ServiceController[] GetServices (string machineName)
+ {
+ ValidateMachineName (machineName);
+
+ using (ServiceController sc = new ServiceController ("dummy", machineName)) {
+ ServiceControllerImpl impl = CreateServiceControllerImpl (sc);
+ return impl.GetServices ();
+ }
+ }
+
+ public void Pause ()
+ {
+ ValidateServiceName (ServiceName);
+ _impl.Pause ();
+ }
+
+ public void Refresh ()
+ {
+ // MSDN: this method also sets the ServicesDependedOn and
+ // DependentServices properties to a null reference
+ //
+ // I assume they wanted to say that the cache for these properties
+ // is cleared. Verified by unit tests.
+ _dependentServices = null;
+ _servicesDependedOn = null;
+ _impl.Refresh ();
+ }
+
+ public void Start ()
+ {
+ Start (new string [0]);
+ }
+
+ public void Start (string [] args)
+ {
+ ValidateServiceName (ServiceName);
+ _impl.Start (args);
+ }
+
+ public void Stop ()
+ {
+ ValidateServiceName (ServiceName);
+ _impl.Stop ();
+ }
+
+ public void WaitForStatus (ServiceControllerStatus desiredStatus)
+ {
+ WaitForStatus (desiredStatus, TimeSpan.MaxValue);
+ }
+
+ public void WaitForStatus (ServiceControllerStatus desiredStatus, TimeSpan timeout)
+ {
+ ValidateServiceName (ServiceName);
+
+ DateTime start = DateTime.Now;
+ while (Status != desiredStatus) {
+ if (timeout < (DateTime.Now - start))
+ throw new TimeoutException ("Time out has expired and the"
+ + " operation has not been completed.");
+ Thread.Sleep (100);
+ // force refresh of status
+ Refresh ();
+ }
+ }
+
+ internal string Name {
+ get {
+ return _name;
+ }
+ set {
+ _name = value;
+ }
+ }
+
+ internal string InternalDisplayName {
+ get {
+ return _displayName;
+ }
+ set {
+ _displayName = value;
+ }
+ }
+
+ internal string InternalServiceName {
+ get {
+ return _serviceName;
+ }
+ set {
+ _serviceName = value;
+ }
+ }
+
+ private static void ValidateServiceName (string serviceName)
+ {
+ if (serviceName.Length == 0 || serviceName.Length > 80)
+ throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
+ "Service name {0} contains invalid characters, is empty"
+ + " or is too long (max length = 80).", serviceName));
+ }
+
+ private static void ValidateMachineName (string machineName)
+ {
+ if (machineName == null || machineName.Length == 0)
+ throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
+ "MachineName value {0} is invalid.", machineName));
+ }
+
+ private static ServiceControllerImpl CreateServiceControllerImpl (ServiceController serviceController)
+ {
+ int p = (int) Environment.OSVersion.Platform;
+
+ if (p == 4 || p == 128 || p == 6){
+ return new UnixServiceController (serviceController);
+ } else {
+ return new Win32ServiceController (serviceController);
+ }
+ }
}
}