Windows Services

A Windows Service is an application that runs as a background process independent of the currently logged on user. Services are supported on Windows NT, Windows 2000, and Windows XP.

The life cycle and runtime configuration of a service is controlled by a component of Windows called the Service Control Manager (SCM) based on configuration information held in the Windows registry. The SCM also controls how other programs can interact with the service.

Service development and deployment are straightforward but lack the elegant simplicity of many aspects of .NET. The complexity stems from the necessity to install the service instead of simply running it like a normal assembly.

The creation of a service involves the development of the following three components: the service application, an installer, and a service controller. We discuss each component individually in the following sections.

The Service Application

Development of the service application involves the following steps:

  1. Derive a class from System.ServiceProcess.ServiceBase that will contain the functionality for the service.

  2. Implement an instance constructor that configures the service and sets the inherited properties of ServiceBase to the correct initial values.

  3. Override the methods inherited from ServiceBase that process life cycle messages from the SCM; all services should override the OnStart method.

  4. Implement a Main method that calls the static ServiceBase.Run method, passing an instance of the service class as an argument.

Despite having a Main method, service code cannot be executed directly; the ServiceBase.Run call causes a Windows Service Start Failure dialog to appear. The service must be installed before it can execute; we cover installation in the next section.

No lengthy processing should be carried out in the Main method or the instance constructor, nor should the service functionality be started until instructed by the SCM.

The creation of a service is best explained by example, so we’ll develop two simple services that exist in a single assembly. Despite being defined in a single assembly, each service is independently controlled via the SCM. We have called one service LogBeacon1 and the other LogBeacon2.

Both the LogBeacon1 and LogBeacon2 services are instances of the BeaconService class that derives from ServiceBase. When instantiated, the BeaconService class creates a thread that periodically writes entries to the Application event log; the activity of the logging thread is controlled by commands from the SCM. The BeaconService code is listed here; a discussion of the code follows.

using System;
using System.Threading;
using System.ServiceProcess;

public class BeaconService : ServiceBase {

    private Thread BeaconThread = null;
    private int Interval = 5000;

    public BeaconService(String name) {
        ServiceName = name;
        CanStop = true;
        CanPauseAndContinue = true;
        BeaconThread = new Thread(new ThreadStart(Beacon));
    }

    protected override void OnStart(String[] args) {
        if (BeaconThread != null) {
            BeaconThread.Start();
        }
    }

    protected override void OnStop() {
        if (BeaconThread != null) {
            BeaconThread.Abort();
        }
    }

    protected override void OnPause() {
        if (BeaconThread != null) {
            BeaconThread.Suspend();
        }
    }

    protected override void OnContinue() {
        if (BeaconThread != null) {
            BeaconThread.Resume();
        }
    }

    private void Beacon() {
        while (true) {
            EventLog.WriteEntry(ServiceName + " active at "
                + DateTime.Now);
            Thread.Sleep(Interval);
        }
    }

    public static void Main() {

        // Create an array to hold instances of each
        // Windows Service
        ServiceBase [] ServiceList = new ServiceBase [2];

        ServiceList[0] = new BeaconService("LogBeacon1");
        ServiceList[1] = new BeaconService("LogBeacon2");

        ServiceBase.Run(ServiceList);
    }
}

The Main method creates two BeaconService instances, one for each of the services. We pass the service name as an argument to the BeaconService constructor; this is used to set the value of the inherited ServiceName property. ServiceName is the name the SCM uses internally to identify the service.

In the BeaconService constructor, we have set the value of the inherited CanStop and CanPauseAndContinue properties to true. The SCM uses these properties to determine the life cycle control messages that the service can process. These properties as well as the CanShutdown and CanHandlePowerEvent properties are summarized in Table A-10.

Table A-10. Life Cycle Control Properties of the ServiceBase Class

Property

Description

CanHandlePowerEvent

Defines whether the service can handle notifications of changes to the computer power status via the OnPowerEvent method.

CanPauseAndContinue

Defines whether the service can be paused and resumed via the OnPause and OnContinue methods.

CanShutdown

Defines whether the service should be notified prior to system shutdown via the OnShutdown method. This is useful if the service needs to store data or state before being terminated.

CanStop

Defines whether the service can be stopped via the OnStop method once it has been started.

The SCM controls the operation of a service via a set of methods inherited from ServiceBase. However, the SCM will call only those methods that the service has indicated it supports via the properties just discussed. Each of these methods is summarized in Table A-11.

Table A-11. Life Cycle Control Methods of the ServiceBase Class

Member

Descirption

OnContinue()

Method called by the SCM to make the service resume processing after being paused. Invoked only if the CanPauseAndContinue property is true.

OnCustomCommand()

Allows the SCM control mechanisms to be extended to support custom commands. OnCustomCommand takes an integer argument that can be used by the service to determine the appropriate action to take.

OnPause()

Method called by the SCM to make the service pause any processing. Invoked only if the CanPauseAndContinue property is true.

OnPowerEvent()

Method called when a system-level power status change occurs, such as a laptop going into suspend mode.

OnShutdown()

Method called when the system is being shut down. Invoked only if the OnShutdown property is true.

OnStart()

Method called by the SCM to start the service. Receives a String array of arguments. All services should override the OnStart method. No lengthy processing should be carried out in the OnStart method; the SCM expects it to return within 30 seconds.

OnStop()

Method called by the SCM to halt the service. Invoked only when the CanStop property is true.

We override the OnStart method to initiate the functionality of the service; we also override the OnStop, OnPause, and OnContinue methods because the CanStop and CanPauseAndContinue properties are set to true. In the example, we use methods of the Thread class to start, abort, suspend, or resume the operation of the logging thread in response to each SCM message.

Finally, in the Main method, references to the new BeaconService instances are placed in a ServiceBase array and passed as an argument to the static ServiceBase.Run method. The Run method calls the SCM and passes it the references to the two services.

Event Logging

The ServiceBase class provides support for automated event logging. If the inherited AutoLog property is set to true (the default value), the ServiceBase constructor creates a System.Diagnostics.EventLog. The EventLog.Source property is set to the ServiceName of the service and is registered against the Application log. See the Windows Event Log section earlier in this appendix for details.

The service will automatically log events when the OnStart, OnStop, OnPause, or OnContinue method of the service is invoked.

The EventLog instance can be obtained using the inherited EventLog property and can be used to write additional entries to the event log; however, if there is a need to write to a log other than the Application log, the AutoLog property should be set to false and the service itself should implement the necessary event-logging functionality.

Service Installer

Services cannot be executed like other assemblies; the tight integration with the operating system and the configuration information held in the Windows registry requires that they be explicitly installed.

A service installer class must be developed and compiled into the assembly containing the service class. Once the installer class is compiled, the Installer Tool (installutil.exe) provided with the .NET SDK is used to install the service.

An installer class must

  • Derive from the System.Configuration.Install.Installer class.

  • Be annotated with the RunInstaller(true) attribute.

  • Create and configure a System.ServiceProcess.ServiceProcessInstaller instance to describe the service application.

  • Create and configure System.ServiceProcess.ServiceInstaller instances to describe each of the individual contained services.

  • Add the ServiceProcessInstaller and ServiceInstaller instances to the InstallerCollection exposed through the inherited Installers property.

Note that a Main method is not required; the installutil tool instantiates the installer, so all logic should be contained in the instance constructor.

The following code provides an installer for the BeaconService service:

using System.ServiceProcess;
using System.Configuration.Install;
using System.ComponentModel;

[RunInstaller(true)]
public class BeaconServiceInstaller : Installer {

    // Create a ServiceProcessInstaller for each application
    private ServiceProcessInstaller BeaconProcess = null;

    // Create a ServiceInstaller instance for each
    // service contained in the application
    private ServiceInstaller BeaconLogService1 = null;
    private ServiceInstaller BeaconLogService2 = null;

    public BeaconServiceInstaller() {

        // Instantiate and configure the ServiceProcessInstaller
        BeaconProcess = new ServiceProcessInstaller();
        BeaconProcess.Account = ServiceAccount.LocalSystem;

        // Instantiate and configure the ServiceInstaller for the
        // First Event Log Beacon
        BeaconLogService1 = new ServiceInstaller();
        BeaconLogService1.DisplayName = "Log Beacon 1";
        BeaconLogService1.ServiceName = "LogBeacon1";
        BeaconLogService1.StartType = ServiceStartMode.Automatic;

        // Instantiate and configure the ServiceInstaller for the
        // Second Event Log Beacon
        BeaconLogService2 = new ServiceInstaller();
        BeaconLogService2.DisplayName = "Log Beacon 2";
        BeaconLogService2.ServiceName = "LogBeacon2";
        BeaconLogService2.StartType = ServiceStartMode.Automatic;

        // Add the installers to the Installers collection
        Installers.Add(BeaconLogService1);
        Installers.Add(BeaconLogService2);
        Installers.Add(BeaconProcess);
    }
}

ServiceProcessInstaller

One System.ServiceProcess.ServiceProcessInstaller instance is required per assembly that contains one or more services. The properties of the ServiceProcessInstaller instance are used to provide configuration information common to all services contained in the assembly. The important properties of the ServiceProcessInstaller class are summarized in Table A-12.

Table A-12. Properties of the ServiceProcessInstaller Class

Property

Description

Account

Controls the account under which the service will run. Specified using a value from the System.ServiceProcess.ServiceAccount enumeration; valid values include User, LocalService, LocalSystem, and NetworkService. These values are described in Table A-13; the default is User.

Username

If the Account property is set to User, this property must contain the name of the user account to run the service as.

Password

If the Account property is set to User, this property must contain the password for the user account.

If the Account property is set to User and the Username and Password properties don’t have values, a dialog box will appear during installation asking for Username and Password values.

Table A-13. Members of the ServiceAccount Enumeration

Value

Description

LocalService

A predefined account that has limited privileges on the local system and that presents anonymous credentials to remote servers during network operations. Available only on Windows XP.

LocalSystem

A predefined account that has extensive privileges on the local system and that presents the computer’s credentials to remote servers during network operations.

NetworkService

A predefined account that has limited privileges on the local system and that presents the computer’s credentials to remote servers during network operations. Available only on Windows XP.

User

The service will run in the context of a specified user.

ServiceInstaller

One System.ServiceProcess.ServiceInstaller instance is required for each service contained in the assembly. The ServiceInstaller properties listed in Table A-14 are used to configure the service.

Table A-14. ServiceInstaller Properties

Property

Description

ServiceName

Specifies the name used to identify the service; this must be the same as the ServiceBase.ServiceName specified in the service application. In the example, we use LogBeacon1 and LogBeacon2.

DisplayName

A user-friendly name to identify the service.

StartType

A value from the System.ServiceProcess.ServicesStartMode enumeration that defines how the service will be started; valid values are Automatic, Disabled, and Manual.

ServicesDependedOn

A String array containing the service names of all services that must be started before the service being defined.

Installer Compilation and Service Installation

Assuming the BeaconService class is in a file named BeaconService.cs and the BeaconServiceInstaller class is in a file named BeaconInstaller.cs, we compile them into an installable assembly named Beacon.exe using the following command:

csc /out:Beacon.exe BeaconService.cs BeaconInstaller.cs

To execute the assembly and install the LogBeacon1 and LogBeacon2 services, use the installutil.exe tool as follows:

installutil Beacon.exe

Using the Services utility available from Administrative Tools in Control Panel, the two services can be seen listed as Log Beacon 1 and Log Beacon 2. Using the Services tool, they can be started, paused, and stopped. The effect of changing the state can be seen as the events are written to the Application event log.

The Beacon services can be uninstalled using the installutil.exe tool with the /u option as follows:

installutil /u Beacon.exe

Service Controller

Various tools included with Microsoft Windows provide basic service configuration and life cycle control. However, services can also be controlled programmatically, often via a service-specific management interface. Implementation of a service controller is the only way to send custom commands to a service via the ServiceBase.OnCustomCommand method described earlier.

The System.ServiceProcess.ServiceController class provides the functionality to query and control installed services on both local and remote machines.

Instances of the ServiceController class represent installed services. The ServiceController instance can be bound to a specified local or remote service during construction or by changing the ServiceName and MachineName properties after instantiation. Changing the MachineName and ServiceName of a bound instance will bind to the newly specified service.

ServiceController also provides the static GetServices method that returns a ServiceController array representing all the services installed on a local or remote machine.

Once a ServiceController is instantiated and bound to an installed service, the members detailed in Table A-15 can be used to control and query the service.

Table A-15. ServiceController Members

Member

Description

Property

 

CanPauseAndContinue

Returns the value of the CanPauseAndContinue property of the service.

CanShutdown

Returns the value of the CanShutdown property of the service.

CanStop

Returns the value of the CanStop property of the service.

DependentServices

Gets a ServiceController array containing the services that depend on this service.

DisplayName

Gets or sets the value of the DisplayName property of the service.

MachineName

Gets or sets the name of the machine where the service is located.

ServiceName

Returns the value of the ServiceName property of the service.

ServicesDependedOn

Gets a ServiceController array containing the services that this service depends on.

Status

Gets the status of the service represented by a value from the System.ServiceProcess.ServiceControllerStatus enumeration.

Method

 

Close()

Closes the ServiceController instance and frees used resources.

Continue()

Executes the OnContinue method of the service; throws InvalidOperationException if the CanPauseAndContinue property of the service isn’t true. Works only if the service Status property is Paused.

ExecuteCommand()

Executes the OnCustomCommand method of the service; an application-specific int argument identifies the command to perform.

Pause()

Executes the OnPause method of the service; throws InvalidOperationException if the CanPauseAndContinue property of the service isn’t true. Works only if the service has a Status of Running.

Start()

Executes the OnStart method of the service. Optionally takes a String array to pass to the service as an argument. Works only if the service Status property is Stopped.

Stop()

Executes the OnStop method of the service; throws InvalidOperationException if the CanStop property of the service isn’t true. Works only if the service Status property is Running.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset