Monitoring and Controlling the Service


To monitor and control services, you can use the Services MMC snap-in that is part of the Computer Management administration tool. With every Windows system, you also get a command-line utility, net.exe, which allows you to control services. Another command-line utility is sc.exe. This utility has much more functionality than net.exe, which is part of the Platform SDK. In this section, you create a small Windows application that makes use of the System.ServiceProcess.ServiceController class to monitor and control services.

MMC Computer Management

Using the Services snap-in to the Microsoft Management Console (MMC), you can view the status of all services (see Figure 22-15). It’s also possible to send control requests to services to stop, enable, or disable them, as well as to change their configuration. The Services snap-in is a service control program as well as a service configuration program.

image from book
Figure 22-15

If you double-click QuoteService, you’ll get the Properties dialog box shown in Figure 22-16. This dialog box enables you to view the service name, the description, the path to the executable, the startup type, and the status. The service is currently started. The account for the service process can be changed with the Log On tab in this dialog.

image from book
Figure 22-16

net.exe

The Services snap-in is easy to use, but the system administrator cannot automate it, because it’s not usable within an administrative script. To control services, you can use the command-line utility net.exe: net start shows all running services, net start servicename starts a service, net stop servicename sends a stop request to the service. It’s also possible to pause and to continue a service with net pause and net continue (only if the service allows it, of course).

Figure 22-17 shows the result of net start in the console window.

image from book
Figure 22-17

sc.exe

There’s a little-known utility delivered with Visual Studio, but starting with Windows XP is also part of the operating system: sc.exe.

sc.exe is a great tool to play with services. A lot more can be done with sc.exe than with the net.exe utility. With sc.exe, it’s possible to check the actual status of a service, or configure, remove, and add services, as Figure 22-18 shows. This tool also facilitates the deinstallation of the service, if it fails to function correctly.

image from book
Figure 22-18

Visual Studio Server Explorer

It is also possible to control services using the Server Explorer within Visual Studio; Services is below Servers and the name of your computer. By selecting a service and opening the context menu, a service can be started and stopped. This context menu can also be used to add a ServiceController class to the project. If you want to control a specific service in your application, drag and drop a service from the Server Explorer to the Designer: a ServiceController instance is added to the application. The properties of this object are automatically set to access the selected service, and the assembly System .ServiceProcess is referenced. You can use this instance to control a service in the same way as you can with the application that you develop in the next section.

ServiceController Class

In this section, you create a small Windows application that uses the ServiceController class to monitor and control Windows Services.

Create a WPF application with a user interface as shown in Figure 22-19. The main form of this application has a list box to show all services, four text boxes to display the display name, status, type, and name of the service, and four buttons to send control events.

image from book
Figure 22-19

Tip 

You can read more about WPF in Chapter 31, “Windows Presentation Foundation.”

Monitoring the Service

With the ServiceController class, you can get the information about each service. The following table shows the properties of the ServiceController class.

Open table as spreadsheet

Property

Description

CanPauseAndContinue

If pause and continue requests can be sent to the service, true is returned.

CanShutdown

Returns true if the service has a handler for a system shutdown.

CanStop

Returns true if the service is stoppable.

DependentServices

Returns a collection of dependent services. If the service is stopped, all dependent services are stopped beforehand.

ServicesDependentOn

Returns a collection of the services that this service depends on.

DisplayName

Specifies the name that should be displayed for this service.

MachineName

Specifies the name of the machine that the service runs on.

ServiceName

Specifies the name of the service.

ServiceType

Specifies the type of the service. The service can be run inside a shared process where more than one service uses the same process (Win32ShareProcess), or run in that way that there’s just one service in a process (Win32OwnProcess). If the service can interact with the desktop, the type is InteractiveProcess.

Status

Specifies the status of the service. The status can be running, stopped, paused, or in some intermediate mode like start pending, stop pending, and so on. The status values are defined in the enumeration ServiceControllerStatus.

In the sample application, the properties DisplayName, ServiceName, ServiceType, and Status are used to display the service information. Also, CanPauseAndContinue and CanStop are used to enable or disable the Pause, Continue, and Stop buttons.

To get all the needed information for the user interface, the class ServiceControllerInfo is created. This class can be used for data binding and offers status information, the name of the service, the service type, and the information which buttons to control the service should be enabled or disabled.

Tip 

Because the class System.ServiceProcess.ServiceController is used, you must reference the assembly System.ServiceProcess.

ServiceControllerInfo contains an embedded ServiceController that is set with the constructor of the ServiceControllerInfo class. There’s also a read-only property Controller to access the embedded ServiceController.

  public class ServiceControllerInfo {    private ServiceController controller;    public ServiceControllerInfo(ServiceController controller)    {       this.controller = controller;    }    public ServiceController Controller    {       get { return controller; }    } 

To display current information about the service, the ServiceControllerInfo class has the read-only properties DisplayName, ServiceName, ServiceTypeName, and ServiceStatusName. The implementation of the properties DisplayName and ServiceName just accesses the properties DisplayName and ServiceName of the underlying ServiceController class. With the implementation of the properties ServiceTypeName and ServiceStatusName more work is done: The status and type of the service cannot be returned that easily because a string should be displayed instead of a number, which is what the ServiceController class returns. The property ServiceTypeName returns a string that represents the type of the service. The ServiceType you get from the property ServiceController.ServiceType represents a set of flags that can be combined by using the bitwise OR operator. The InteractiveProcess bit can be set together with Win32OwnProcess and Win32ShareProcess. So, at first it is checked if the InteractiveProcess bit is set before continuing to check for the other values. With services the string returned will be “Win32 Service Process” or “Win32 Shared Process”:

  public string ServiceTypeName {    get    {       ServiceType type = controller.ServiceType;       string serviceTypeName = "";       if ((type & ServiceType.InteractiveProcess) != 0)       {          serviceTypeName = "Interactive ";          type -= ServiceType.InteractiveProcess;       }       switch (type)       {          case ServiceType.Adapter:             serviceTypeName += "Adapter";             break;          case ServiceType.FileSystemDriver:          case ServiceType.KernelDriver:          case ServiceType.RecognizerDriver:             serviceTypeName += "Driver";             break;          case ServiceType.Win32OwnProcess:             serviceTypeName += "Win32 Service Process";             break;          case ServiceType.Win32ShareProcess:             serviceTypeName += "Win32 Shared Process";             break;          default:             serviceTypeName += "unknown type " + type.ToString();             break;       }       return serviceTypeName;    } } public string ServiceStatusName {    get    {       switch (controller.Status)       {          case ServiceControllerStatus.ContinuePending:             return "Continue Pending";          case ServiceControllerStatus.Paused:             return "Paused";          case ServiceControllerStatus.PausePending:             return "Pause Pending";          case ServiceControllerStatus.StartPending:             return "Start Pending";          case ServiceControllerStatus.Running:             return "Running";          case ServiceControllerStatus.Stopped:             return "Stopped";          case ServiceControllerStatus.StopPending:             return "Stop Pending";          default:             return "Unknown status";       }    } } public string DisplayName {    get { return controller.DisplayName; } } public string ServiceName {    get { return controller.ServiceName; } } 

The ServiceControllerInfo class has some more properties to enable the Start, Stop, Pause and Continue buttons: EnableStart, EnableStop, EnablePause, and EnableContinue. These properties return a Boolean value according to the current status of the service:

     public bool EnableStart    {       get       {          return controller.Status == ServiceControllerStatus.Stopped;       }    }        public bool EnableStop    {       get       {          return controller.Status == ServiceControllerStatus.Running;       }    }    public bool EnablePause    {       get       {          return controller.Status == ServiceControllerStatus.Running &&                controller.CanPauseAndContinue;       }    }    public bool EnableContinue    {       get       {          return controller.Status == ServiceControllerStatus.Paused;       }    } } 

In the ServiceControlWindow class, the method RefreshServiceList() gets all the services using ServiceController.GetServices() for display in the list box. The GetServices() method returns an array of ServiceController instances representing all Windows Services installed on the operating system. The ServiceController class also has the static method GetDevices() that returns a ServiceController array representing all device drivers. The returned array is sorted with the help of the generic Array .Sort() method. The sort is done by the DisplayName as is defined with the anonymous method that is passed to the Sort() method. Using Array.ConvertAll(), the ServiceController instances are converted to the type ServiceControllerInfo. Here, an anonymous method is passed that invokes the ServiceControllerInfo constructor for every ServiceController object. Last, the ServiceControllerInfo array is assigned to the DataContext property of the window for data binding.

  protected void RefreshServiceList() {    ServiceController[] services = ServiceController.GetServices();    Array.Sort<ServiceController>(services,       delegate(ServiceController s1, ServiceController s2)       {          return s1.DisplayName.CompareTo(s2.DisplayName);       });    ServiceControllerInfo[] serviceInfo =       Array.ConvertAll<ServiceController, ServiceControllerInfo>(       services,       delegate(ServiceController controller)       {          return new ServiceControllerInfo(controller);       });    this.DataContext = serviceInfo; } 

The method RefreshServiceList() to get all the services in the list box is called within the constructor of the class ServiceControlWindow. The constructor also defines the event handler for the Click event of the buttons:

  public ServiceControlWindow() {    InitializeComponent();    buttonStart.Click += OnServiceCommand;    buttonStop.Click += OnServiceCommand;    buttonPause.Click += OnServiceCommand;    buttonContinue.Click += OnServiceCommand;    buttonRefresh.Click += OnRefresh;    buttonExit.Click += OnExit;    RefreshServiceList(); } 

Now, you can define the XAML code to bind the information to the controls.

First, a DataTemplate is defined for the information that is shown inside the ListBox. The ListBox will contain a Label where the Content is bound to the DisplayName property of the data source. As you bind an array of ServiceControllerInfo objects, the property DisplayName is defined with the ServiceControllerInfo class:

  <Window.Resources>   <DataTemplate x:Key="listTemplate">     <Label Content="{Binding Path=DisplayName}"/>   </DataTemplate> </Window.Resources> 

The ListBox that is placed in the left side of the Window sets the ItemsSource property to {Binding}. This way the data that is shown in the list is received from the DataContext property that was set in the RefreshServiceList() method. The ItemTemplate property references the resource listTemplate that is defined with the DataTemplate shown earlier. The property IsSynchronizedWithCurrentItem is set to True, so that the TextBox and Button controls that are inside the same Window are bound to the current item that is selected with the ListBox.

  <ListBox Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left"       Name="listBoxServices" VerticalAlignment="Top"       ItemsSource="{Binding}"       ItemTemplate="{StaticResource listTemplate}"       IsSynchronizedWithCurrentItem="True"> </ListBox> 

With the TextBox controls, the Text property is bound to the corresponding property of the ServiceControllerInfo instance. Whether the Button controls are enabled or disabled is also defined from the data binding by binding the IsEnabled property to the corresponding properties of the ServiceControllerInfo instance that return a Boolean value:

  <TextBox Grid.Row="0" Grid.ColumnSpan="2" Name="textDisplayName"     Text="{Binding Path=DisplayName, Mode=OneTime}" /> <TextBox Grid.Row="1" Grid.ColumnSpan="2" Name="textStatus"     Text="{Binding Path=ServiceStatusName, Mode=OneTime}" /> <TextBox Grid.Row="2" Grid.ColumnSpan="2" Name="textType"     Text="{Binding Path=ServiceTypeName, Mode=OneTime}" /> <TextBox Grid.Row="3" Grid.ColumnSpan="2" Name="textName"     Text="{Binding Path=ServiceName, Mode=OneTime}" /> <Button Grid.Row="4" Grid.Column="0" Name="buttonStart" Content="Start"     IsEnabled="{Binding Path=EnableStart, Mode=OneTime}" /> <Button Grid.Row="4" Grid.Column="1" Name="buttonStop" Content="Stop"     IsEnabled="{Binding Path=EnableStop, Mode=OneTime}" /> <Button Grid.Row="5" Grid.Column="0" Name="buttonPause" Content="Pause"     IsEnabled="{Binding Path=EnablePause, Mode=OneTime}" /> <Button Grid.Row="5" Grid.Column="1" Name="buttonContinue" Content="Continue"     IsEnabled="{Binding Path=EnableContinue, Mode=OneTime}" /> 

Controlling the Service

With the ServiceController class, you can also send control requests to the service. The following table explains the methods that can be applied.

Open table as spreadsheet

Method

Description

Start()

Start() tells the SCM that the service should be started. In the example service program OnStart() is called.

Stop()

Stop() calls OnStop() in the example service program with the help of the SCM if the property CanStop is true in the service class.

Pause()

Pause() calls OnPause() if the property CanPauseAndContinue is true.

Continue()

Continue calls OnContinue() if the property CanPauseAndContinue is true.

ExecuteCommand()

With ExecuteCommand() it’s possible to send a custom command to the service.

The following code controls the services. Because the code for starting, stopping, suspending, and pausing is similar, only one handler is used for the four buttons:

  protected void OnServiceCommand(object sender, RoutedEventArgs e) {    Cursor oldCursor = Cursor.Current;    Cursor.Current = Cursors.Wait;    ServiceControllerInfo si =           (ServiceControllerInfo)listBoxServices.SelectedItem;    if (sender == this.buttonStart)    {       si.Controller.Start();       si.Controller.WaitForStatus(ServiceControllerStatus.Running);    }    else if (sender == this.buttonStop)    {       si.Controller.Stop();       si.Controller.WaitForStatus(ServiceControllerStatus.Stopped);    }    else if (sender == this.buttonPause)    {       si.Controller.Pause();       si.Controller.WaitForStatus(ServiceControllerStatus.Paused);    }    else if (sender == this.buttonContinue)    {       si.Controller.Continue();       si.Controller.WaitForStatus(ServiceControllerStatus.Running);    }    int index =listBoxServices.SelectedIndex;    RefreshServiceList();    listBoxServices.SelectedIndex = index;    Cursor.Current = oldCursor; } protected void OnExit(object sender, RoutedEventArgs e) {    Application.Current.Shutdown(); } protected void OnRefresh_Click(object sender, RoutedEventArgs e) {    RefreshServiceList(); } 

Because the action of controlling the services can take some time, the cursor is switched to the wait cursor in the first statement. Then a ServiceController method is called depending on the pressed button. With the WaitForStatus() method, you are waiting to check that the service changes the status to the requested value, but you only wait 10 seconds maximum. After this time, the information in the ListBox is refreshed, and the same service as before is selected, and the new status of this service is displayed.

Figure 22-20 shows the completed, running application.

image from book
Figure 22-20




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net