The .NET Framework classes include a set of base classes, in the System.ServiceProcess namespace, that provide the underlying functionality of a Windows service application. Visual Studio .NET offers a project template that automatically sets a reference to System.ServiceProcess and also provides you some boilerplate code. This section describes the default setup in detail. When you create a project by using the template, you need to concentrate only on the unique features that your application will implement.
When you create a new project in Visual Studio .NET and choose Windows Service as your project template, the project will initially look like Figure 1.2. The default project contains one component class module (with the default name Service1.vb). If you view the code inside Service1.vb (see Listing 1.1), you will notice that a class has been created (also using the default class name Service1). This class inherits from the System.ServiceProcess.ServiceBase namespace. The template also adds an Imports statement for the System.ServiceProcess namespace.
Figure 1.2: Visual Studio .NET’s default project setup for a Windows Service application
Listing 1.1: Default Code for a Windows Service Application
Imports System.ServiceProcess Public Class Service1 Inherits System.ServiceProcess.ServiceBase 'Component Designer generated code appears here Protected Overrides Sub OnStart(ByVal args() As String) ' Add code here to start your service. This method ' should set things in motion so your service can ' do its work. End Sub Protected Overrides Sub OnStop() ' Add code here to perform any teardown necessary ' to stop your service. End Sub End Class
If you expand the References node in the Solution Explorer window, you can see that a reference has been added for System.ServiceProcess.dll. Note that the .dll suffix is not present.
The default code also contains two procedure definitions for important methods of the ServiceBase class, OnStart and OnStop. You will add your custom code to these, and other methods, to implement the specific behavior of your Windows service application.
If you expand the region titled Component Designer Generated Code, you will see implementations for the New and Dispose methods, with code specific to how these standard Framework methods should be coded for a Windows service. There is also a Sub Main() procedure with some code needed for a Windows service to be started correctly (see Listing 1.2). The code in this procedure calls the Run method of the ServiceBase class and passes a reference to a new instance of your service. This is the code that enables your service to start when the operating system or a user invokes it.
Listing 1.2: Component Designer Generated Code
' The main entry point for the process <MTAThread()> _ Shared Sub Main() Dim ServicesToRun() As _ System.ServiceProcess.ServiceBase ' More than one NT Service may run in the same ' process. To add another service to this process, ' change the following line to create a second ' service object. For example, ' ' ServicesToRun = New _ ' System.ServiceProcess.ServiceBase() _ ' {New Service1, New MySecondUserService} ' ServicesToRun = New System.ServiceProcess.ServiceBase() _ {New Service1} System.ServiceProcess.ServiceBase.Run(ServicesToRun) End Sub
Now that you have seen the basics required to create a Windows service application, you can concentrate on creating a service with custom functionality. To do this, you will provide custom implementations for methods of the parent ServiceBase class (see Table 1.1). The ServiceBase class also defines properties that you can set to affect the behavior of your service (see Table 1.2).
Method Name | Description |
---|---|
OnContinue | Implement this method to run custom code when a service is resumed after being paused. |
OnCustomCommand | Implement this method when you need custom actions that can be called programmatically by a ServiceController object. |
OnPause | Implement this method to run custom code when a service is paused. |
OnPowerEvent | Implement this method to run custom code when the computer’s power status has changed—for example, a laptop computer going into suspended mode. |
OnShutdown | Implement this method to run custom code before the computer shuts down. |
OnStart | Implement this method to run custom code when a service starts. It is preferred to put initialization code in this procedure rather than in the constructor (Sub New method). |
OnStop | Implement this method to run custom code when a service is stopped. |
Tip | You will see how to implement the OnCustomCommand method later in this chapter, in the section titled “Executing Custom Commands for a Service.” |
Note | It is preferred to use the OnStart method for any code that must run when your service is started. Code in the constructor method, Sub New, runs when the service is instantiated, before it is completely started and running in the context of the Service Control Manager. Also, the Visual Studio .NET documentation states that “there is no guarantee the objects will be reinitialized when you restart a service after it has been stopped.” |
Property Name | Description |
---|---|
AutoLog | If this property is set to True, every time the service is started, stopped, paused, or continued, an entry will be written to the Windows Application event log. Set this property to False if you want to code custom log messages. |
CanHandlePowerEvent | Set this to True if you have written custom code for the OnPowerEvent method. This will enable you to take special action if the computer that your service is running on experiences a change in power status—for example, a laptop computer going into suspended mode. |
CanPauseAndContinue | Set this value to True if you want to allow your service to be paused. |
CanShutdown | Set this to True if you have written custom code for the OnShutdown method. This will enable you to take special action before the computer shuts down. |
CanStop | This value is usually set to True. It is set to False for some important operating system services, which should not be stopped by a user. |
EventLog | If the AutoLog property is set to True, messages will be written to the Windows Application event log. If you set AutoLog to False, then you can specify a different event log for messages. |
ServiceName | Gets or sets the service name. |
The Project Installers are “helper” classes that you add to your Windows service project. They provide important information that is used during the installation of your service application, such as the name that will be displayed in the Service Control Manager console, whether the service is started automatically or manually, and the security account. The security account is a Windows user login or system account that provides the identity and permissions that the Windows service will run with. Each Windows service project will have one instance of the ServiceInstaller class and one instance of the ServiceProcessInstaller class for each service that is included in the project.
When you are working in the Visual Studio .NET Integrated Development Environment (IDE), you can add ServiceInstaller components directly to your project from the Toolbox.
Note | If you prefer, you can also create these objects in code. You will see an example of that in Chapter 10, “Deploying, Securing, and Configuring Windows-Based Applications.” |
A Windows service runs independently of any user who might be logged onto the computer; therefore, the service must have a security identity of its own. When you create a Windows service application, you can select from one of four options for the security identity:
User You create a specific username and password (using the standard Windows tools for doing so) for your application. Provide this username and password during installation. You must also provide this user with the appropriate permissions to complete the work of the Windows service application.
LocalSystem LocalSystem is a built-in Windows account. It is the most commonly used setting for Windows services. It is a highly privileged account and is seen by other servers as an anonymous account.
LocalService This is a built-in Windows account. It provides limited privileges on the local computer and is seen by other computers on the network as an anonymous user, so it is unlikely that code running under this identity will be allowed access to resources on other computers on the network. This account is available only on Windows XP and later operating systems.
NetworkService This is a built-in Windows account. It runs with limited privileges on the local computer and can communicate with other servers as an authenticated domain account. This account is available only on Windows XP and later operating systems.
Again, the most commonly used security identity is LocalSystem. This built-in Windows account has a high level of privileges on the computer system. However, it is considered good security practice for applications to run with the least privileges required to perform their work. For example, do not allow the privilege to write to the system Registry if that is not needed to perform the core function of the service. To provide stronger security options, Windows XP and later operating systems have two new built-in accounts: LocalService and NetworkService. These two accounts have fewer privileges assigned to them by default. When installing a Windows service application, you should determine the level of privilege required and choose the best account.
Note | These security accounts and other security considerations are discussed more thoroughly in Chapter 10. |
Unlike most .NET projects, you cannot run a Windows service application directly from the Visual Studio .NET IDE by choosing Debug Ø Start from the main menu (or its equivalent toolbar or keystroke shortcuts). If you try to do this, you will see a message box that reads:
“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.”
What this means is that you cannot interactively run your application for testing from within the Visual Studio .NET IDE. That is the way most Visual Basic .NET developers are used to working, and it’s very convenient. Working with Windows service applications is a bit more structured.
You must first build and install your Windows service before you can test and debug it to see whether it is working correctly. Although this seems like a big drawback to developing this type of application, keep in mind that a Windows service application runs in a different context than regular user applications. It runs in the context of the Service Control Manager and under a different security context than the user identity that you are logged in as during development. To debug a Windows service application, you must complete the application, install it, and then attach a debugger to the running process.
We cover the steps to attaching a debugger to the process later in this chapter, in the section “Debugging a Windows Service.” In Exercise 1.2, you will create a simple Windows service application. The steps for creating a setup project that will perform the installation of the service are included in the exercise. For a full explanation of creating setup and deployment projects, see Chapter 10.
Tip | For practical purposes, in real-world Windows service applications, you will probably want to create a Console or Windows Forms application to interactively test specific program logic before you add the code to your Windows service. After you are satisfied that your test code is working correctly, you can add it to the methods of your Windows service project. |
For your first Windows service, you are going to design a simple service that uses a custom event log to record information about when the service is started and stopped.
You will create a new Windows service application project called CustomLogService. Next you will change some properties of the component. You will also add EventLog and Installer components from the Toolbox to the project. Finally, you are going to add code to the OnStart and OnStop events and also to the constructor method, Sub New.
Exercise 1.2: Creating a Windows Service by Using Visual Studio .NET
Setting Up the Project:
Start Visual Studio .NET and create a new project by using the Windows Service project template. Name the project CustomLogService and select an appropriate directory on your computer.
Using the Solution Explorer, rename the component Service1.vb to CustomLogService.vb.
Click on the design surface of CustomLogService.vb and display the Properties window. Change both the Name property and the Service name property to CustomLogService. Change the AutoLog property to False. Change the CanStop property to True.
Display the Visual Studio .NET Toolbox and click the Components tab. Drag an EventLog component onto the design surface.
Click the EventLog component and display the Properties window. Change the name to CustomEventLog.
Adding Code:
Open the code editor for CustomLogService.vb. Verify that the class is named CustomLogService and that it inherits from System.ServiceProcess.ServiceBase:
Public Class CustomLogService Inherits System.ServiceProcess.ServiceBase
Expand the region titled Component Designer Generated Code. Add code to the New procedure. Code to initialize the custom event log is placed in the New procedure, instead of OnStart, because you want this code to run only when the Windows service is first installed, rather than each time it is restarted. Your completed code should look like this:
Public Sub New() MyBase.New() ' This call is required by the Component Designer. InitializeComponent() ' Add any initialization after InitializeComponent() If Not EventLog.SourceExists("CustomSource") Then EventLog.CreateEventSource("CustomSource", "CustomLog") End If CustomEventLog.Source = "CustomSource" CustomEventLog.Log = "CustomLog" End Sub
Add code to the OnStart and OnStop event procedures. Here you will write an entry to the custom event log to keep track of when the service is stopped and started. Your code should look like this:
Protected Overrides Sub OnStart(ByVal args() As String) CustomEventLog.WriteEntry("The service has been started.") End Sub Protected Overrides Sub OnStop() CustomEventLog.WriteEntry("The service has been stopped.") End Sub
Adding Installer Components:
Click the design surface of CustomLogService.vb and display the Properties window. Near the bottom of the Properties window is a link titled Add Installer. Click this link, and a new component class module called ProjectInstaller.vb will be added to your project. You will see the design surface for this component has two other component icons on it: ServiceProcessInstaller1 and ServiceInstaller1.
Click ServiceProcessInstaller1 and display the Properties window. Select the Account property. Choose LocalSystem from the drop-down list. (If you decide to have your service running under a user account, you would also fill in the necessary information in the Password and Username properties here.)
Click ServiceInstaller1 and display the Properties window. Select the StartType property. Choose Automatic from the drop-down list.
Building the Service:
Before you can build the service, you need to clean up some details.
Display the Task List window by choosing View Ø Other Windows Ø Task List from the menu. You will most likely see two errors; the first one says “Type Service1 is not defined.” There is a remaining reference to the default name Service1.
Double-click this entry in the Task List window, the code editor window will display the section of code where the error is located and the line of code that is in error will be highlighted. Change Service1 to CustomLogService.
The next item in the Task List says “’Sub Main’ was not found in ‘CustomLogService.Service1’”. This refers to the project Startup Object. Double-click this entry in the Task List window, and a dialog box pops up showing the new correct reference to CustomLogService.CustomLogService. Select this item and click OK.
Now you can build the CustomLogService. Right-click the project name in the Solution Explorer and choose Build, or choose Build Ø Build CustomLogService from the menu.
Save the CustomLogService project. You will be using it for future exercises.
Creating a Setup Project to Install the Service:
Many details are involved in creating a setup project and deploying Windows service applications. This topic is covered in more detail in Chapter 10. The following instructions are designed to get your new application up and running quickly so you can test it.
In the Solution Explorer, click on the solution. Choose File Ø Add Project Ø New Project from the Visual Studio menu.
In the Add New Project dialog box, select Setup and Deployment Projects and select the Setup Project template. Name the new project CustomLogSetup. Click OK.
In the Solution Explorer, right-click CustomLogSetup. Choose Add Ø Project Output from the menu. The Add Project Output Group dialog box displays. Select Primary Output and click OK.
In the Solution Explorer, right-click CustomLogSetup again. Choose View Ø Custom Actions from the menu.
In the upper-left corner of the work area, right-click Custom Actions. Choose Add Custom Action. The Select Item in Project dialog box displays. Double-click Application Folder, select Primary Output from CustomLogService (Active), and click OK. Your screen should look like the following one.
Build the setup project. Right-click the project name in the Solution Explorer and choose Build, or choose the menu command Build Ø Build CustomLogSetup.
Save the CustomLogSetup project, because you will be using it again later in this chapter.
Installing and Testing the Service:
In the Debug subdirectory of the CustomLogSetup project directory, you will find a Windows Installer file named CustomLogSetup.msi. Double-click this file to start the installation.
This will start a Setup Wizard. Accept all the defaults and complete the installation.
Run the Service Control Manager to verify that your service is installed. To do this, click Start Ø Programs Ø Administrative Tools Ø Services (or the appropriate sequence for your operating system version). You should see CustomLogService in the list.
Right-click on your service and choose Properties. Start your service.
Click Start Ø Programs Ø Administrative Tools Ø Event Viewer (or the appropriate sequence for your operating system version) to view your custom event log in the Event Viewer.
Click the log named CustomLog.Then right-click any one of the log entries and choose Properties (or just double-click the entry). You will see your custom message in the Properties dialog box.