Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Simple Windows Service in .NET with Console Mode

4.27/5 (6 votes)
8 Jan 2010CPOL3 min read 42.9K   1.4K  
A framework for implementing simple Windows services with an eye towards usability.

Introduction

Windows services are handy things to have around. Unfortunately, Microsoft doesn't make it simple to write your own. What is more, they can be difficult to debug during development.

This article presents a simple framework for creating your own Windows services. It accepts several command line parameters to make installing, starting, stopping, and uninstalling the service a snap. Also, it has a console mode to make debugging easy.

Background

I spent a lot of time wading through pages and pages, site after site, of example code for developing Windows services. None of the samples out there really did everything I wanted. Some employed considerable hacks, and there were none that separated the service bits from the actual worker bits. This project contains what I consider to be the bare minimum features for a usable Windows service, with a simple interface for adding your worker code.

Using the Code

This is setup for Visual Studio 2008 and .NET 3.5. Some modifications would be needed to use this code with .NET 2.0, e.g., get rid of lambda functions, but it could easily be done.

ISimpleServiceWorker

All you have to do is inherit the ISimpleServiceWorker interface and implement it in your worker class.

C#
using System;

namespace RPW.Simple
{
    interface ISimpleServiceWorker
    {
        void Init();
        void Run();
        void Cleanup();
    }
}

Initialize your resources in the Init() method and clean up your resources in the Cleanup() method. The Run() method would typically contain a loop that manages the state of your task. You don't have to create a thread; the SimpleProject class will create the necessary thread. You should keep in mind that the thread will be terminated by the Thread.Abort() method. The ThreadAbortException will be caught in the SimpleProject class that creates the thread context, so you don't need to catch it, but you could if you have a reason. In order to keep the shutdown time reasonable, try not to make blocking calls, e.g., sockets.

Console Mode

Services are notoriously difficult to debug. For this reason, this project provides for a console mode so that you can run your code without all of the service overhead. Simply pass the -console command line switch to your service executable, and Shazaam! You're running in console mode. In console mode, the static WriteLog method of the SimpleService class directs logging to the console window instead of the application event log.

Managing Your Service

The SimpleService class provides several command line switches:

PS C:\rev\src\simpleservice\src\bin\release> .\SimpleService.exe -help
= usage:
=   SimpleService -install   == install service
=   SimpleService -uninstall == uninstall service
=   SimpleService -start     == start service
=   SimpleService -stop      == stop service
=   SimpleService -status    == get the current status of the service
=   SimpleService -console   == run in console mode
=   SimpleService -help      == show this help message
PS C:\rev\src\simpleservice\src\bin\release>

Using these command line switches, you can install and uninstall your service without using InstallUtil.exe. Also, you can start and stop your service once it is installed. The -status switch will tell you if your service is installed, started, or stopped.

Nuances

The project uses the process name as the service name. You could change this in code, but you must make sure that the same name is applied to the ServiceBase and the ServiceInstaller. For simplicity, use the static ServiceName property of the SimpleServiceInstaller class.

The project assumes that your service needs LocalSystem privileges and starts automatically. This will invoke the Windows Vista and Windows 7 UAC dialogs when you use the service. You can change those defaults in the SimpleServiceInstaller class.

C#
public SimpleServiceInstaller()
{
    // setup and add the process installer
    m_processInstaller = new ServiceProcessInstaller()
        {
            Account = ServiceAccount.LocalSystem
        };
    Installers.Add(m_processInstaller);

    // setup and add the service installer
    m_serviceInstaller = new ServiceInstaller()
    {
        StartType = ServiceStartMode.Automatic,
        // ServiceName must equal those on ServiceBase derived classes.
        ServiceName = ServiceName,
        DisplayName = ServiceName,
        Description = ServicePath
    };
    Installers.Add(m_serviceInstaller);
}

Conclusions

I think you will be pleased with this design. Simply implement a worker class by inheriting the ISimpleServiceWorker interface, change the project assembly name, and build it! The command line switches make everything else a snap. Please don't hesitate to notify me of bugs or oversights. And by all means, enjoy!

History

  • January 2010 - Initial submission.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)