Monday, March 30, 2009

How to create (and deploy) a windows service in C# ?

 

Let's say we have the following specifications :

  • We want an application that will turn continuously on a machine
  • This application shall require no manual interaction (starting automatically)
  • This application shall monitor a specific folder on the file

The specifications are very simple and we will probably start thinking to a windows service that would be launched in an automatic manner. This service could then use a FileSystemWatcher to do its job.

Let's create our service

  • Create a new Blank Solution
  • Add a new project of type "Windows Service" (this type is available under the Visual C# / Windows section) and let's call it MyWindowsService
  • Automatically, VS will create a new project containing a class called "Service1", inheriting from System.ServiceProcess.ServiceBase
  • Right-Click on the file and choose Rename. Rename it to MyWindowsService
  • Update the constructor to set a service name.
using System.ServiceProcess; 
  
namespace MyWindowsService 
{ 
   public partial class MyWindowsService : ServiceBase 
   { 
      public MyWindowsService() 
      { 
         InitializeComponent(); 
         this.ServiceName = "MyPersonalService"; 
      } 
  
      protected override void OnStart(string[] args) 
      { 
      } 
  
      protected override void OnStop() 
      { 
      } 
   } 
} 

This class exposes a parameterless constructor and two methods OnStart and OnStop. These are the two methods that will be called when the service is being started (or stopped) by Windows. Note that automatically, when a service is being started or stopped, Windows will log an entry in the event viewer.

Let's check this !

Let's deploy our (empty) service 

To do so, we'll need to create a setup project and some installation actions to parameter the way the service is started.

  • Add a new project of type "Setup project(this type is available under Other Project Types / Setup and deployment)
  • Let's now how add a reference to our project
    • Right-Clic on the setup project and choose "Add / Project Output"
    • Select the project "MyWindowsService", the "Primary Output" and the configuration "Release"
    • Click on OK 

We will need now to control the way the service will be started and to specify it is a service. For that we have two solutions, either we rely on the service itself, or we write it ourself. We will here only see how to create the installer thru the service itself. 

Creating the installer's action class via the Service class itself

  • Double-Click on "MyWindowsService.cs" and right-clic in the designer to choose "Add Installer"
  • This will add a new file called "ProjectInstaller.cs" that contains a "serviceInstaller" and a "serviceProcessInstaller"
  • Check the property of the ServiceInstaller
    • Change the ServiceName to "MyPersonalService" (shall be the same as what you specified in the service constructor)
    • Change the DisplayName to "My Personal Service"
    • Change the StartType to "Automatic"
  • Check the property of the ServiceProcessInstaller
    • Change the Account to... well any value depending of the behaviour you would like. In my case, I will choose "LocalService". You can check the Service.ServiceProcess.ServiceAccount class for more information about this. 

Let's now complete the installer

Now we can complete the installer to add the installer actions we have just created.

  • Right click on the setup project and choose "View / Custom Actions"
  • Now we need to add our installer class to be used for the events "Install" and "Uninstall"
    • Right-Clic on Install and choose "Add Custom Actions"
    • Double clic on Application Folder
    • Select the "Primary Output from MyWindowsService" and click OK
    • Do the same thing for the event "Uninstall"

Now we just need to compile and to install the service to check that it is working.

  • Set yourself in "Release Mode"
  • Right-Clic on the Setup project and choose "Rebuild"
  • Right-Clic on the project and choose "Install"

Let's check the behaviour

As I have said before a service will log some information when being started and stopped. We can so go in the "Services" management window.

 

Note that even if we have put the "start type" to "Automatic", you must start the service manually. It would start automatically only after the next windows reboot !

Let's start the service and check in the Event Viewer.

We can see two new events saying respecively

  • The My Personal Service service was succesfully sent a start control
  • The My Personal Service service entered the running state

So far so good !

Let's complete our service to let him work

Now that we have a functional service, we can complete him to work. We wanted him to monitor a folder. Let's modify our service and do that this way:

using System.IO; 
using System.ServiceProcess; 
  
namespace MyWindowsService 
{ 
   public partial class MyWindowsService : ServiceBase 
   { 
      public MyWindowsService() 
      { 
         InitializeComponent(); 
         this.ServiceName = "MyPersonalService"; 
      } 
  
      protected override void OnStart(string[] args) 
      { 
         //1. Let's check only txt files in a specific folder 
         string path = @"c:\Temp\MyFolderToMonitor"; 
         string filter = "*.txt"; 
         FileSystemWatcher watcher = new FileSystemWatcher(path, filter); 
  
         //2. We want information about the filename 
         watcher.NotifyFilter = NotifyFilters.FileName; 
  
         //3. Let's register the creation event only, and let's start the monitring 
         watcher.Created += new FileSystemEventHandler(watcher_Created); 
         watcher.EnableRaisingEvents = true; 
      } 
  
      ///  
      /// When a file is created, we'll just add an entry in the EventLog 
      /// Note that this.EventLog will log to the Application Log,  
      /// with the name of the service as source. 
      /// To log in another log, you should create yourself an EventLog. 
      ///  
      private void watcher_Created(object sender, FileSystemEventArgs e) 
      { 
         string message = string.Format("{0} : {1}", e.ChangeType, e.Name); 
         this.EventLog.WriteEntry(message); 
      } 
  
      protected override void OnStop() 
      { 
      } 
   } 
} 

Now we can just rebuild and reinstall the service. We can then start it, create a file in the "Temp\FolderToMonitor" folder and stop the service. If we now look to the EventViewer, what can we see ? Three events :

  • Service started successfully
  • Created: Document1.txt
  • Service stopped successfully

And here we are ! We now have a fully functional windows service.