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|
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.
|
6.5 Creating a Windows Service Controller Application
Add a new Windows Application project to the solution. Name the project StepByStep6_5. 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. 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.

Add a reference to the System.ServiceProcess.dll to the project. Add the following using directive to the code:
using System.ServiceProcess;
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";
}
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;
}
}
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;
}
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;
}
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.

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. 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. 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.
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.
|
|