Controlling a Windows Service



Controlling a Windows Service

Create and manipulate a Windows service.

In the previous section, I discussed several utilities for manipulating Windows services. You may also need to manipulate a Windows service programmatically from your program. You may use this in the scenario where you want to take some action based on the status of a service or you may want to take some actions in a program that require you to start, pause, continue, or stop a Windows service. You may also want to programmatically manipulate a Windows service when you want to provide your own custom user interface for configuring and controlling a Windows service.

NOTE

Creating MMC Snap-ins If you wish to provide an MMC-integrated administration module for your Windows service application, you can use classes from the System.Management.Instrumentation namespace. These classes make up a whole subject known as Windows Management Instrumentation (WMI), which is not a part of the Exam 70-320 objectives. However, if you are curious you can find more information in the "Managing Applications Using WMI" topic in the .NET Framework SDK documentation.


You can use the ServiceController class of the System.ServiceProcess namespace to programmatically control Windows services from your programs. In the next two sections, you'll learn what the ServiceController class is and how you can use this class in your programs.

The ServiceController Class

The ServiceController class provides functionality to connect to a Windows service and control its behavior. Figure lists some important methods and properties of this class that you'll typically use in a Windows service control program.

Figure Important Members of the ServiceController Class

Member

Type

Description

CanPauseAndContinue

Property

A value of true indicates that the service can be paused and resumed.

CanShutdown

Property

A value of true indicates that the service should be notified when the system is shutting down.

CanStop

Property

A value of true indicates that the service can be stopped after it has started.

Close()

Method

Disconnects the ServiceController object from the Windows service.

Continue()

Method

Continues a Windows service after it has been paused.

DisplayName

Property

Specifies a friendly name for the service.

ExecuteCommand()

Method

Executes a custom command on the service.

GetServices()

Method

Retrieves the Windows services installed on a computer.

MachineName

Property

Specifies the name of the computer on which this service resides. Its default value is set to the local computer. You need to change the MachineName property only if you want to control the Windows services on a remote machine.

Pause()

Method

Suspends a Windows service's operation.

Refresh()

Method

Refreshes the values of all the properties with their latest values.

ServiceName

Property

Specifies the name of the Windows service.

ServicesDependedOn

Property

Specifies the set of services on which this Windows service depends.

ServiceType

Property

One of the ServiceType enumeration values that specifies how the Windows service is used. A Windows service can be of type—InteractiveProcess (can communicate with the desktop), Win32OwnProcess (runs in its own process), or Win32ShareProcess (shares the process with other Windows services).

Start()

Method

Starts the Windows service.

Status

Property

Retrieves the status of the Windows service.

Stop()

Method

Stops the Windows service and other services that are dependent on this service.

WaitForStatus()

Method

Waits for the service to reach the specified status, which is a value of the ServiceControllerStatus enumeration. Its possible values are: ContinuePending, Paused, PausePending, Running, StartPending, Stopped, and StopPending

Creating a Windows Service Controller Application

In this section, I'll show you how to use various methods and properties of the ServiceController class to create a simple service controller application.

The application enumerates the list of installed services on the local machine and enables users to start, stop, pause, continue, and refresh the list of services. Step-by-Step 6.5 shows you how to accomplish this.

STEP BY STEP

6.5 Creating a Windows Service Controller Application

  1. Add a new Windows Application project to the solution. Name the project StepByStep6_5.

  2. Rename the default Form1.cs file to WindowsServiceController.cs. Access the properties window for this form and change the Name property to WindowsServiceController and the Text property to Windows Service Controller. Switch to the code view and change all instances of Form1 to WindowsServiceController.

  3. Add a ListBox control (lbServiceList), a TextBox control (txtStatus), and 4 Button controls (btnStart, btnStop, btnPauseContinue, and btnRefresh) on the form. Arrange the controls as shown in Figure.

    12. Arrange controls to create a user interface for the service controller program.

    graphics/06fig12.jpg

  4. Add a reference to the System.ServiceProcess.dll to the project. Add the following using directive to the code:

    
    using System.ServiceProcess;
    
    
  5. Double-click on an empty area on the form to attach an event handler with its Load event. Add the following code to its event handler:

    
    private void WindowsServiceController_Load
    
                 (object sender, System.EventArgs e)
    
    {
    
        PopulateServiceList();
    
    }
    
    
    
    // Get the list of installed services
    
    protected void PopulateServiceList()
    
    {
    
        // Populate services in the list box
    
        lbServiceList.DataSource =
    
            ServiceController.GetServices();
    
        // Display friendly name
    
        lbServiceList.DisplayMember = "DisplayName";
    
    }
    
    
  6. Double-click on the ListBox to attach an event handler with its SelectedIndexChanged event. Add the following code to its event handler:

    
    private void lbServiceList_SelectedIndexChanged
    
                 (object sender, System.EventArgs e)
    
    {
    
        // Retrieve the selected service
    
        ServiceController service =
    
           (ServiceController) lbServiceList.SelectedItem;
    
        // and get status for it
    
        GetServiceStatus(service);
    
    }
    
    
    
    // Find the latest service status
    
    protected void GetServiceStatus(ServiceController sc)
    
    {
    
        // Get the service status
    
        ServiceControllerStatus status = sc.Status;
    
        // Enable all buttons
    
        btnStart.Enabled = btnStop.Enabled =
    
            btnPauseContinue.Enabled = true;
    
        // Disable the pause button if it does
    
        // not support pause and continue messages
    
        if (!sc.CanPauseAndContinue)
    
            btnPauseContinue.Enabled = false;
    
        // Disable the stop button if it does
    
        // not support stop message
    
        if (!sc.CanStop)
    
            btnStop.Enabled = false;
    
        // Enable and disable buttons based on
    
        // service status, also display the text
    
        // for the status
    
        switch (status)
    
        {
    
            case ServiceControllerStatus.ContinuePending:
    
                txtStatus.Text = "Continue Pending";
    
                btnPauseContinue.Enabled = false;
    
                break;
    
            case ServiceControllerStatus.Paused:
    
                txtStatus.Text = "Paused";
    
                btnPauseContinue.Text = "Continue";
    
                btnStart.Enabled = false;
    
                break;
    
            case ServiceControllerStatus.PausePending:
    
                txtStatus.Text = "Pause Pending";
    
                btnPauseContinue.Enabled = false;
    
                btnStart.Enabled = false;
    
                break;
    
            case ServiceControllerStatus.Running:
    
                txtStatus.Text = "Running";
    
                btnPauseContinue.Text = "Pause";
    
                btnStart.Enabled = false;
    
                break;
    
            case ServiceControllerStatus.StartPending:
    
                txtStatus.Text = "Start Pending";
    
                btnStart.Enabled = false;
    
                break;
    
            case ServiceControllerStatus.Stopped:
    
                txtStatus.Text = "Stopped";
    
                btnStop.Enabled = false;
    
                break;
    
            case ServiceControllerStatus.StopPending:
    
                txtStatus.Text = "Stop Pending";
    
                btnStop.Enabled = false;
    
                break;
    
            default:
    
                txtStatus.Text = "Unknown Status";
    
                break;
    
        }
    
    }
    
    
  7. Add the following event handler and attach it to the Click event of btnStart, btnStop, and btnPauseContinue buttons.

    
    // Common event handler for the Click event on
    
    // start, stop, and pause/continue buttons
    
    private void btnControl_Click
    
         (object sender, System.EventArgs e)
    
    {
    
        // It might take time, so change the
    
        // cursor to a wait cursor
    
        Cursor.Current = Cursors.WaitCursor;
    
        // Retrieve the selected service
    
        ServiceController service =
    
            (ServiceController)
    
            lbServiceList.SelectedItem;
    
        if (sender == this.btnStart)
    
        {
    
            // Start the service and wait till
    
            // the status is Running
    
            service.Start();
    
            service.WaitForStatus
    
                (ServiceControllerStatus.Running);
    
        }
    
        else if (sender == this.btnStop)
    
        {
    
            // Stop the service and wait till
    
            // the status is Stopped
    
            service.Stop();
    
            service.WaitForStatus
    
                (ServiceControllerStatus.Stopped);
    
        }
    
        else if (sender == this.btnPauseContinue)
    
        {
    
            if (btnPauseContinue.Text == "Pause")
    
            {
    
                // Pause the service and wait till
    
                // the status is Paused
    
                service.Pause();
    
                service.WaitForStatus
    
                    (ServiceControllerStatus.Paused);
    
            }
    
            else
    
            {
    
                // Resume the service and wait till
    
                // the status is Running
    
                service.Continue();
    
                service.WaitForStatus
    
                    (ServiceControllerStatus.Running);
    
            }
    
        }
    
        // Refresh the list
    
        int intIndex = lbServiceList.SelectedIndex;
    
        PopulateServiceList();
    
        lbServiceList.SelectedIndex = intIndex;
    
        // Change the cursor back to normal
    
        Cursor.Current = Cursors.Default;
    
    }
    
    
  8. Add the following event handler to the Click event of the btnRefresh control:

    
    private void btnRefresh_Click
    
            (object sender, System.EventArgs e)
    
    {
    
        // Refresh the list
    
        int intIndex = lbServiceList.SelectedIndex;
    
        PopulateServiceList();
    
        lbServiceList.SelectedIndex = intIndex;
    
    }
    
    
  9. Build the StepByStep6_5 project. Set the project as the startup object. Run the project. You will see a window displaying a list of installed services, as shown in Figure.

    13. The Windows Service Controller form controls the Windows services programmatically.

    graphics/06fig13.jpg

  10. Scroll through the list of services. Select OrderService. Pause the service and then continue. Stop the service and then start. As you do this, also create new XML files in the c:\orders directory. You should find that the files are not processed when the OrderService is paused or stopped.

  11. Open the Application EventLog. You'll see that each time you start, pause, continue, or stop the OrderService service, an entry in the Application event log is written because you set the AutoLog property for the Windows service to true.

  12. Click on the Refresh button to update the list of installed services with any newly installed or uninstalled services.

In Step-by-Step 6.5, most of the code is written to make the user interface work logically. For Windows service control, I am using the ServiceController.GetServices() static method to get a list of installed services that I populate in the lbServiceList control.

When the user clicks on the btnStart button, I am calling the Start() method to start the selected Windows service. The Windows service might take some time to start, and meanwhile I should restrict the user from pressing any other buttons; therefore I am calling the synchronous WaitForStatus() method that waits till the Windows service returns a Running status. I followed a similar logic for other control messages.

REVIEW BREAK

  • Several tools are available for monitoring and controlling Windows services. Some notable tools are the Services MMC snap-in, Visual Studio .NET Server Explorer, the net.exe command-line utility, and the Service Control command-line utility (sc.exe).

  • Service Control utility (sc.exe) is distributed as a part of Windows XP and later operating systems and as a part of Win32 SDK and the .NET Framework SDK. This utility is the most powerful tool for controlling and configuring Windows services.

  • Some programs may also need to monitor Windows services programmatically. The FCL provides the System.ServiceProcess.ServiceController class to programmatically control the Windows services.

  • The ServiceController.GetServices() method is used to enumerate installed services on a computer. You can use the Start(), Stop(), Pause(), and Continue() methods to change the status of a Windows service. The WaitforStatus() synchronous method is useful when you would like the current thread to pause execution and wait for the Windows service to enter in the specified state.