Creating a Windows Service Application



Creating a Windows Service Application

Create and manipulate a Windows service: Write code that is executed when a Windows service is started or stopped.

In this section, you use the ServiceBase class to create a Windows service application. You'll learn

  • About the ServiceBase class and its functionality.

  • How the SCM interacts with a Windows service derived from the ServiceBase class.

  • How to create a Windows service by deriving a class from the ServiceBase class.

The System.ServiceProcess.ServiceBase Class

Programmatically, a Windows service is a class that derives its basic functionality from the ServiceBase class of the System.ServiceProcess namespace. The ServiceBase class provides its derived classes with some well-known methods and properties. The SCM knows these methods and properties and uses them to communicate with the Windows services. Figure lists the important members of the ServiceBase class.

Figure Important Members of the ServiceBase Class

Member Name

Type

Description

AutoLog

Property

When set to true, automatically logs the call to OnStart(), OnStop(), OnPause(), and OnContinue() methods in the Application event log.

CanPauseAndContinue

Property

When set to true, indicates that the service can be paused and resumed.

CanShutdown

Property

When set to true, indicates that the service should be notified when the system is shutting down.

CanStop

Property

When set to true, indicates that the service can be stopped after it has started.

EventLog

Property

Specifies an event log that can be used to write customized messages to the Application event log on method calls such as OnStart() and OnStop().

OnContinue()

Method

Specifies the actions to take when a service resumes normal functioning after being paused. This method is executed when the SCM sends a continue message to the Windows service.

OnPause()

Method

Specifies the actions to take when a service pauses. This method is executed when the SCM sends a pause message to the Windows service.

OnStart()

Method

Specifies the actions to take when a service starts running. This method is executed when the SCM sends a start message to the Windows service.

OnStop()

Method

Specifies the actions to take when a service stops running. This method is executed when the SCM sends a stop message to the Windows service.

Run()

Method

Provides the main entry point for a service executable.

ServiceName

Property

Specifies a name that identifies the service.

EXAM TIP

Writing to a Custom Event Log With the help of the AutoLog and the EventLog properties of the ServiceBase class, you can write messages to the Application event log only. If you want to write entries to a different event log, you should set AutoLog to false and create a new EventLog component and override the OnStart(), OnStop(), and other related methods to explicitly post entries to the custom log.


Understanding How the SCM Interacts with a Windows Service

A Windows service application contains one or more classes that inherit from the ServiceBase class to define a Windows service.

Only one of these classes can define a Main() method, which is the main entry point for the application. The Main() method specifies which Windows services should run by passing the instances of the Windows service to the static ServiceBase.Run() method.

The Run() method does not actually start the Windows service; instead, the Run() method just passes references to the Windows service objects to the SCM. The SCM uses these objects to send messages to the Windows service.

Figure illustrates how the SCM interacts with a Windows service derived from the ServiceBase class.

Figure. The ServiceBase class provides the functionality that enables a Windows service application to interact with the SCM.

graphics/06fig02.gif

When the SCM sends a start message to the Windows service, the following things happen:

  1. The SCM finds the path of the Windows service application's executable file from the Windows service database.

  2. The SCM creates a Windows service process by executing the Main() method defined in the Windows service executable.

  3. The Main() method of the Windows service executable creates one or more instances of the Windows service class and passes their references to the SCM through the static Run() method of the ServiceBase class. The SCM uses these references to send messages to the Windows service.

  4. The handler associated with the start message—the OnStart() method—is executed. The OnStart() method executes the code that is required for a Windows service to run, such as listening to a port for incoming requests, logging events to the event log, spooling print jobs, and so on.

After the Windows service is started, the SCM can use its reference to send various messages such as pause, continue, and stop to the Windows service. These messages respectively invoke the OnPause(), OnContinue(), and OnStop() methods of the Windows service.

Not all services implement the OnPause(), OnContinue(), and OnStop() methods. Whether a service needs to implement these methods depends on the CanPauseAndContinue and CanStop properties of the Windows service. If these properties are true, then the SCM can invoke the OnPause(), OnContinue(), and OnStop() methods. Some common examples of Windows services that can neither be paused nor be stopped include the Event Log, the Plug and Play, and the Security Accounts Manager services. Examples of Windows services that can be stopped but cannot be paused include the Print Spooler and the System Event Notification services.

NOTE

Controlling a Windows Service If you set any of the CanStop, CanShutdown, or CanPauseAndContinue properties for a Windows service to true, then you also must implement a corresponding handler method (such as OnStop(), OnPause(), OnContinue(), and so on). Otherwise, when the SCM sends any of these messages, the Windows service throws an exception and ignores the message.


There is no CanStart property in the ServiceBase class. This means that not starting is not an option for a Windows service. A Windows service class must provide a OnStart() method; otherwise the base class version of the method is called.

When the SCM sends a stop message to the Windows service, the associated handler OnStop() method is executed. After this, the Windows service process is unloaded from memory.

Creating the OrderService Application

To demonstrate how Windows services are created, I'll create an OrderService that listens for XML files containing order information in a particular disk directory (c:\orders). As soon an XML file is created in the directory, the service reads the files and based on its data, creates a new record in the Orders table of the Northwind database. This type of application is typically used for updating orders received from the Internet, from business partners, and from the legacy applications.

Step-by-Step 6.1 shows how to create the OrderService application.

STEP BY STEP

6.1 Creating a Windows Service Application

  1. Launch Visual Studio .NET. Select File, New, Blank Solution, and name the new solution 320C06. Click OK.

  2. Add a new project to the solution. In the Add New Project dialog box, select Visual C# .NET as the project type and Windows Service as the template. Name the project StepByStep6_1, as shown in Figure.

    3. The Windows Service template allows you to easily create a Windows service application.

    graphics/06fig03.jpg

  3. The Windows service project contains a file named Service1.cs. Rename this file to OrderService.cs. Click on the designer surface and then in the Properties window, set the Name and ServiceName properties to OrderService, and set the CanPauseAndContinue property to true. Note that the AutoLog and the CanStop properties are already true, as shown in Figure.

    4. You can set the properties of a Windows service through the Properties window.

    graphics/06fig04.jpg

  4. Switch to the code view and change all occurrences of Service1 to OrderService. Add the following using directives to the code:

    
    using System.Data.SqlClient;
    
    using System.IO;
    
    
  5. Switch to the design view. Open the Visual Studio .NET toolbox. From its Components tab (see Figure), drag and drop the FileSystemWatcher component onto the OrderService component's surface.

    Figure. The FileSystemWatcher component listens to the file system and raises events when there are any changes.

    graphics/06fig05.jpg

  6. Access the Properties window for the FileSystemWatcher component and change its Name property to fswOrders, its Filter property to *.xml, and its Path property to c:\orders.

  7. Click on the Events icon on the Properties window for the FileSystemWatcher component and double-click on the Created event to attach an event handler. Add the following code to the event handler:

    
    // Executes when an XML file is created in or
    
    // copied to the c:\orders directory
    
    private void fswOrders_Created(object sender,
    
             System.IO.FileSystemEventArgs e)
    
    {
    
        DataSet dsOrders = new DataSet("Orders");
    
        // Read the contents of the XML file
    
        // into the Orders DataSet
    
        dsOrders.ReadXml(e.FullPath);
    
    
    
        // set up the database connection string
    
        string strConn = "data source=(local);" +
    
            "initial catalog=Northwind;" +
    
            "integrated security=SSPI";
    
        string strOrderQuery = "SELECT * FROM Orders";
    
        // Create a DataAdapter for the Orders
    
        // table of the Northwind database
    
        SqlDataAdapter daOrders =
    
            new SqlDataAdapter(strOrderQuery, strConn);
    
        // Automatically generate the update
    
        // command to reconcile the changes
    
        // made to the DataSet
    
        SqlCommandBuilder cbOrders =
    
            new SqlCommandBuilder(daOrders);
    
        //Update the DataSet
    
        daOrders.Update(dsOrders, "Orders");
    
        daOrders.Dispose();
    
    
    
        FileInfo fi = new FileInfo(e.FullPath);
    
        // Create a subdirectory named "Updated"
    
        // if it does not already exist
    
        fi.Directory.CreateSubdirectory("Updated");
    
        // Copy the processed XML file to updated
    
        // directory, overwriting if needed
    
        File.Copy(e.FullPath, fi.DirectoryName +
    
            @"\Updated\" + fi.Name, true);
    
        // Delete the XML file from its
    
        // original location
    
        fi.Delete();
    
    }
    
    
  8. In the code, search for the skeletons of the OnStart() and OnStop() methods and modify them as follows:

    
    protected override void OnStart(string[] args)
    
    {
    
        fswOrders.EnableRaisingEvents = true;
    
    }
    
    
    
    protected override void OnStop()
    
    {
    
        fswOrders.EnableRaisingEvents = false;
    
    }
    
    
  9. Add the following code to the OrderService class to override the OnPause() and the OnContinue() methods of the base class:

    
    protected override void OnPause()
    
    {
    
        fswOrders.EnableRaisingEvents = false;
    
    }
    
    
    
    protected override void OnContinue()
    
    {
    
        fswOrders.EnableRaisingEvents = true;
    
    }
    
    
  10. Set the startup object for the project to OrderService.

  11. Build the project. This step creates the OrderService Windows service application.

When you create a new project based on the Windows Service template, Visual Studio .NET automatically inserts a class that derives from the System.ServiceProcess.ServiceBase class in the project. To respond to various events raised by the SCM messages, I have overridden the base class version of the OnStart(), OnStop(), OnPause(), and OnContinue() methods.

In Step-by-Step 6.1, I'm using the FileSystemWatcher component. This component watches for any changes in the file system and raises events when a file or directory changes. In this example, I have set up the FileSystemWatcher component in such a way that it just watches for the creation of XML files in a directory named c:\orders. I have also instructed the FileSystemWatcher component to exclude any subdirectories of c:\orders to narrow down the scope of notifications.

When an XML file is created in the c:\orders directory, the FileSystemWatcher component raises the Created event. I am using the fswOrders_Created() event handler to retrieve the order information from the newly created XML file and insert this information as a new order to the Orders table of the Northwind database.

Figure lists some important members of the FileSystemWatcher component.

Figure Important Members of the FileSystemWatcher Class

Member

Type

Description

Changed

Event

Occurs when a file or directory in the specified Path is changed.

Created

Event

Occurs when a file or directory in the specified Path is created.

Deleted

Event

Occurs when a file or directory in the specified Path is deleted.

EnableRaisingEvents

Property

Specifies whether the component is enabled. If its value is false, the component does not receive any events.

Error

Event

Occurs when the component's internal buffer overflows.

Filter

Property

Specifies which files are monitored in a directory. Its default value is *.*, which monitors for all types of files.

IncludeSubdirectories

Property

Specifies whether the subdirectories within the specified path should be monitored.

InternalBufferSize

Property

Specifies the size of the internal buffer.

NotifyFilter

Property

Specifies the type of file system changes to monitor. Its value is a combination of values from the NotifyFilters enumeration.

Path

Property

Specifies the path of the directory to watch.

Renamed

Event

Occurs when a file or directory in the specified Path is renamed.

SynchronizingObject

Property

Specifies the object that is used to marshal the event handler calls.

WaitForChanged()

Method

This method is a synchronous method that returns a structure that contains specific information on the file system change.

WARNING

The FileSystemWatcher Component May Lose Track of Changes The FileSystemWatcher component stores the file system changes in a property named InternalBufferSize before it acts upon those changes. If there are too many changes in a short time, this buffer can overflow and cause the FileSystemWatcher component to lose track of the file system changes. This buffer's default size is 8192 bytes. You should not arbitrarily increase this buffer's size because this buffer is maintained in non-paged memory and increasing its size would directly impact the performance of other applications. Instead, you should use the Path, NotifyFilter, and IncludeSubdirectories properties to narrow down the scope of notifications that the FileSystemWatcher component receives.


Compilation of the StepByStep6_1 project creates an executable file just like other Windows applications. However, you cannot run this executable directly. If you try doing so, you get a Windows service start failure error with the following message:


Cannot start service from the command line or a

debugger. A Windows Service must first be installed

(using installutil.exe) and then started with the

Server Explorer, Windows Services Administrative

tool or the NET START command

Apparently, you need to install a Windows service application before you can start it, and you'll learn how to do that in the following section.