Windows Posts

  • Per Lundberg (gravatar)

    There is this saying that “there is no computer system with a decent level of self-respect that doesn’t include at least one Windows Service”. Generally, this rule seems to be pretty true. Even if you try to build as much as possible of the system with a platform such as Microsoft BizTalk Server  or Windows SharePoint Services (because of personal or customer-specified preferences), there are likely to occur certain parts of the system that are simply not feasible or practical to implement within the boundaries of the platform.

    Here are a few technical scenarios where a Windows Service could be suitable:

    • You want to perform some background processing at certain time intervals, and no other job scheduler is available. For example, you might want to wake up every 5 minutes and poll a database, and perform some operations for each record found. Such scenarios can also be considered being implemented with BizTalk Server or SQL Server Integration Services (SSIS), if available.
    • You want to listen to data arriving on a certain input channel, and do some form of processing every time data arrives. The input channel could be a TCP socket (like port 80 or 21), an MSMQ queue, or anything else. This scenario is also a valid candidate for being implemented with BizTalk.

    However, we now assume that you have investigated the alternatives and decided that a Windows Service is the most suitable option in your case.

    Writing a Windows Service is not that difficult, especially with the help provided by the .NET Framework all the way back since version 1.1 (Visual Studio 2003). Still, there are some things that could be worthwhile to think about when doing this. Some of these practices relate to the actual coding of the service, and some relate to how the service is best being deployed and set up on the server you are deploying it to.

    The target audience of this guide is anyone wanting to write a Windows Service, having previous experience with the .NET Framework and writing code in C#. You don’t need to have any previous experience with writing Windows Services per se. For those of you who have such experience, this guide might feel too simple for you; still, there might be some new and useful ideas to be found here even for you.

    Let’s start with the basics . We create a new Solution and Project in Visual Studio (I’m using 2008 in my examples, but this should work with 2005 or 2010 as well):

    New Project

    The “New Project” dialogue in Visual Studio

    As you see, you find the Windows Service project template under Visual C# –> Windows. You could here also choose another version of the .NET Framework, such as 2.0, if you were to install this service onto a server which limits you in terms of which version of the .NET Framework is installed.

    I prefer to set the solution name here to a the namespace “root” for the projects within this solution, and let the new project use the same namespace with “.Service” appended to it.

    When you click OK, you get a skeleton class that looks like this (Service1.cs, “View Code”):

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    
    namespace eCraft.Labs.WindowsServiceSkeleton.Service
    {
        public partial class Service1 : ServiceBase
        {
            public Service1()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
            }
    
            protected override void OnStop()
            {
            }
        }
    }

    In other words; a simple Windows Service, that doesn’t really do anything. :-)

    Now, what do we do first? Well, for starters, rename the Service1 class to something more useful. (You should really never keep the default file names such as Service1, Class1 etc. in Visual Studio, but always rename the files to something more appropriate.) If you want to make it simple, just call it “Service”. I just checked one of our own Windows Services, and it’s called just that. ;-)

    OK, the project has been created and you are now probably eager to see how you can get this program running as a Windows Service, right? Before we do so, let’s do something important first: we want to change the service’s visible name. This name is the name that will be seen in the Windows Services list (services.msc), and also something that you can refer to using the “net start/net stop” commands (more about those later). Since it becomes a bit awkward to change this name later on, it’s better to do it now, before you install the service into Windows. You find the “visible name” of the service in the “Design View” of the Service1 class (or whatever you renamed it to). Take up the Design View and look for the Properties dialogue:

    Properties

    As you see, the ServiceName is listed at the very end of this dialog. Change it to something appropriate for your specific need – I strongly recommend having a name without spaces here (the reason for this will be discussed in a later post), like WindowsServiceSkeleton in my case

    Now, it is time to compile the solution (Ctrl-Shift-B). You should now get an .exe file in the Debug folder called eCraft.Labs.WindowsServiceSkeleton.Service.exe:

    image

    The compiled results of the project.

    To get this program installed now as a service, you would do like this: Start up the Visual Studio 2008 command prompt (or whichever Visual Studio version you are using). Change to the bin\Debug directory. Your command prompt should now be something like this:

    Command Prompt

    Now, to make this service be installed into the Windows Service list, all you need to do is this:

    installutil /i <name of .exe file>

    When you run this command, the output should look something like this:

    installutil output

    Results of running installutil.

    It looks as if it has failed (“No public installers with the RunInstallerAttribute.Yes attribute could be found”), and… this is actually correct! We forgot something important: for the service to be installable, you need to have an installer class in it. Let’s go back to Visual Studio and choose “Add New Item” to our project. This time, choose this item template:

    Add New Item

    The Add New Item dialogue.

    The naming of this file is not so easy, actually. If your service is called Service.cs, two logical names would be either ServiceInstaller.cs or Installer.cs. The problem is that both of these names would cause name clashes with other classes that this class needs to use. We call it ProjectInstaller.cs for now (of course, if you called your own service class PizzaService.cs, you could always call the installer PizzaServiceInstaller.cs as well…).

    If you are an observative person, you might recognize that Visual Studio adds a reference to the System.Configuration.Install assembly when you press the Add button. This is because the ServiceInstaller class derives from a base class in that assembly.

    The code for the ProjectInstaller.cs class looks like this:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Configuration.Install;
    using System.Linq;
    
    
    namespace eCraft.Labs.WindowsServiceSkeleton.Service
    {
        [RunInstaller(true)]
        public partial class ProjectInstaller : Installer
        {
            public ProjectInstaller()
            {
                InitializeComponent();
            }
        }
    }

    This will be enough to get the service installable, but… the Service itself will not be installed. This is not done automatically; we need to write installation code for it. So, we modify the file to look something like this:

    using System.ComponentModel;
    using System.Configuration.Install;
    using System.ServiceProcess;
    
    namespace eCraft.Labs.WindowsServiceSkeleton.Service
    {
        [RunInstaller(true)]
        public partial class ProjectInstaller: Installer
        {
            public ProjectInstaller()
            {
                InitializeComponent();
    
                ServiceProcessInstaller processInstaller = new ServiceProcessInstaller();
                ServiceInstaller serviceInstaller = new ServiceInstaller();
    
                // Run the service as Local System, with automatic startup. These could be modified as needed for your specific
                // service. (The account and start type can always be modified afterwards through the Windows Services MMC console;
                // these are just the defaults.)
                processInstaller.Account = ServiceAccount.LocalSystem;
                serviceInstaller.StartType = ServiceStartMode.Automatic;
                
                // This must be the same as the value we specified earlier, in the in Service.cs "Design View".
                serviceInstaller.ServiceName = "WindowsServiceSkeleton";
    
                // These are the values that will be displayed int he Windows Services MMC console.
                serviceInstaller.DisplayName = "Windows Service Skeleton";
                serviceInstaller.Description =
                    "This service acts as a Windows Service Skeleton, helping people who are learning how to write good Windows " +
                    "services.";
                
                Installers.Add(serviceInstaller);
                Installers.Add(processInstaller);   
            }
        }
    }

    (Of course, customize the DisplayName and Description the way you like.) Let’s build the solution again, and go back to our command prompt. Re-run the previous command. This time, it should work much better:

    installutil output

    The service seems to have been installed correctly. Let’s look it up in the list of Windows Services (tip: you can easily get to the list of Windows services by pressing Win-R (for “Run”), and typing “services.msc” in the textbox).

    image

    Yahoo! It’s there. Cool, huh? Let’s do one more thing before we end this Part 1 of the Windows Service series of blog postings: we start the service. This time, we make it simple (I’ll show you another way to do it in the next blog posting); we just click on the green “play” arrow in the MMC console. If all works out well, the service will now start, and the Status will be changed to “Started”. You can also look in the event log and you’ll see that the service was started:

    Event viewer 

    Well, that’s it for now! You can now stop the service in the Services MMC console.

    We have now gone through the basics of creating a fully functional (but yet meaningless, since it doesn’t do anything) Windows Service, that can be successfully installed using the installutil command. In the next blog posting, we’ll move on to make the service actually do something useful… :-) Until then, I wish you all a great Christmas holiday.