Click here to Skip to main content
15,860,844 members
Articles / Programming Languages / C#
Article

Self installing .NET service using the Win32 API

Rate me:
Please Sign up or sign in to vote.
4.73/5 (21 votes)
28 Oct 2005CPOL5 min read 174.9K   1.6K   90   53
Sometimes the service classes provided by Visual Studio don't give you the control you need, so why not build your own? And while you're at it, why not make it self-installing? The base class provided gives you full control of the Win32 Services API from a convenient base class and attribute.

Test service in Service Manager

Introduction

The Windows service code that ships with the .NET framework and Visual Studio works just fine usually. However, sometimes it's just annoying to have to create an installer project just for a simple service you're writing. Furthermore, Microsoft tends to hide away the Win32 service infrastructure from us. I'm not saying that's a bad thing, but sometimes you just need something a little better. There're some advanced issues with services that make working with the API difficult and so wouldn't it be nice to have something that encapsulates all the functionality, but exposes it to those who need it? The code provides a base class and an attribute for you to use to define your own service.

Background

A thorough discussion of the intricacies of Win32 services is another subject. This code focuses on bringing it all together and then exposing it via an easy to use class and custom attribute.

Using the code

Using the code is as simple as deriving from the base class:

C#
using System;
using HoytSoft.ServiceProcess.Attributes;

namespace MyServices {
    public class TestService : HoytSoft.ServiceProcess.ServiceBase {

        protected override bool Initialize(string[] Arguments) {
            this.Log("Test service initialized correctly, starting up...");
            return true;
        }
    }
}

And then defining an attribute:

C#
using System;
using HoytSoft.ServiceProcess.Attributes;

namespace MyServices {
    [Service(
    "HoytSoft_TestService", 
    DisplayName         = "HoytSoft Test Service", 
    Description         = "Isn't this just absolutely amazing?",
    ServiceType         = ServiceType.Default,
    ServiceAccessType   = ServiceAccessType.AllAccess,
    ServiceStartType    = ServiceStartType.AutoStart,
    ServiceErrorControl = ServiceErrorControl.Normal,
    ServiceControls     = ServiceControls.Default
    )]
    public class TestService : HoytSoft.ServiceProcess.ServiceBase {

        protected override bool Initialize(string[] Arguments) {
            this.Log("Test service initialized correctly, starting up...");
            return true;
        }
    }
}

Test service in Service Manager

All the attributes correspond to their equivalent Win32 service descriptions. A lot of the code has been commented, so a glance at the intellisense should give you an idea of what each option is and how it modifies your Windows service.

It is important that you give a name to your service. It is a required parameter for the Service attribute. The attribute values specified here are used in the ServiceBase's Main() method. I suppose I should also note that your service should not define its own Main() method since ServiceBase uses its own that reads in Service attributes and then creates objects automatically for you. If the service has not been installed, it will auto-install it for you. To uninstall a service, simply pass in "u" or "uninstall" to the program. To manually do this, go to a DOS prompt and type in: sc delete "MyServiceName".

An interesting feature that hasn't been tested at all is the ability to use multiple services in a single application. To do this, be sure to set the ServiceType to ServiceType.ShareProcess or ServiceType.InteractiveProcessShare.

Now of most importance to you is probably developing, testing, and debugging your service. There is a property on the Service attribute named "Debug". Set this to true and the base class will automatically treat your program like a normal console program so you can develop the rest of the program. When you're ready to test it out as an actual service running on the machine, just switch this to false or take it out and it will work like a service. The program will install the service whether or not you're in debug mode when it's first run. If you try to run the program when it was compiled in Debug mode, you will get an error saying the process couldn't be started. This is by design since to gain all of the debug mode capabilities in Visual Studio, you can't have it start up as a real service. Simply switch the Debug mode, recompile, and it will run as normal.

You can write to the system event log by making a call to this.Log("My message here."):

The main meat of your code should be the overridden methods:

  • bool Initialize(string[] Arguments)

    "Arguments" are those passed in when called.

  • Start()
  • Stop()
  • Pause()
  • Continue()
  • Shutdown()
  • Interrogate()
  • Install()
  • Uninstall()

All of these should be fairly straightforward. Any questions?

Updates

  • 10/4/05: After the default timeout of 30 seconds (this is a Windows-imposed standard), if the service is not being run by the service control manager (SCM), the program runs like a normal console/Windows program. This is done in the ServiceBase's main method which will detect when it's not being run as a service and executes the proper methods on the service.

    You just continue to use the service like you normally would through the Initialize() and Start() methods. If you want to test other methods, you can optionally use the following methods from inside Start():

    • TestPause()
    • TestContinue()
    • TestStop()
    • TestShutdown()
    • TestInterrogate()
    • TestInstall()
    • TestUninstall()

    ServiceBase will take care of calling your methods while attempting to simulate a real situation or user by calling your overridden methods asynchronously.

  • 10/12/05: A message was added to the CodeProject article informing me of missing copyright notices in the source code, so I've added them here along with some more updates. This version has a major fix involving running it from a referenced, external DLL. If you put the code inside another DLL and then try to use it, you may notice you can start, but can't pause or stop your service. The fix, actually, was just adding a strong name key (HoytSoft.snk) to the external assembly. After I did this, I was able to run the service normally.

    As a result, I have divided the solution into two projects - one containing the service code and the other is just a simple example console service app.

    Important! The namespace has changed! It was HoytSoft.Service, but it is now HoytSoft.ServiceProcess to more accurately reflect its replacement of the System.ServiceProcess namespace. Please keep in mind that to install/run the service you must have the proper permissions!

  • 10/21/05: There were some more problems with referencing the ServiceBase class from a class library. I think the main problem was a delegate that was going out of scope that rendered a callback useless. Lots of people got errors when trying to pause, stop, or do anything on the service when using it as a class library. I was able to reproduce the problem and after making the aforementioned fixes, it worked just fine.

    Also of note is the ability to explicitly and only install the service without having to run the rest of the service. To do this, just start the service and pass in "i" or "install" and it will quickly install and exit. If you start the app normally and it hasn't been installed yet, the app will work like before and install it before proceeding. You should still be able to run it as a console or service app.

Points of Interest

  • The service installs itself and can uninstall itself through a command line argument ("u" or "uninstall").
  • You can run, test, and debug your service from within Visual Studio without having to create a separate installer project or use the ServiceController classes.

History

  • Took out "Debug" property from the ServiceAttribute attribute since the code now auto-detects this. (See "Points of Interest" above).

License

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


Written By
Software Developer (Senior) Lawrence Livermore National Laboratories
United States United States
I'm a recent graduate of Brigham Young University in Provo, UT and now working for Lawrence Livermore National Laboratories (LLNL). I've been programming since I was 14 and did the amazing Hoyt family website with an animated gif of a spinning globe. I've come a long way since then and now actually use pictures of people.

I've been interested in website development and Windows programming since and I haven't stopped except for two years spent in El Salvador as a religious representative for my church.

I've done lots of work with C#/C/C++/Java/Python/JavaScript/Scheme/T-SQL/PL-SQL/Visual Basic/etc., web services, windows apps, services, and web apps. It's been a lot of fun!

Comments and Discussions

 
GeneralMy vote of 5 Pin
cyberghost7712-Apr-11 2:34
cyberghost7712-Apr-11 2:34 
QuestionRunning a Process from within the service Pin
Krischu9-Nov-09 6:22
Krischu9-Nov-09 6:22 
Any clues, why I cannot execute the test.bat file (access denied) in the example code?
Running a compiled C-program instead of the test.bat file runs just fine.


<br />
<br />
	  protected override void Start() {<br />
			this.Log("Service started");<br />
            ProcessStartInfo startInfo = new ProcessStartInfo();<br />
<br />
            startInfo.FileName = "c:\\test.bat";<br />
            startInfo.Arguments = " 12345";<br />
            startInfo.WorkingDirectory = @"C:\Temp";<br />
            startInfo.WindowStyle = ProcessWindowStyle.Maximized;<br />
            startInfo.ErrorDialog = true;<br />
<br />
           Process process;<br />
<br />
        <br />
<br />
            process = Process.Start(startInfo);<br />
<br />
            <br />
<br />
            if (process.WaitForExit(30000))<br />
            {<br />
                this.Log("Process terminated.");<br />
            }<br />
            else<br />
            {<br />
                this.Log("Timed out waiting for process to end.");<br />
<br />
            }<br />
        <br />
<br />
}<br />

QuestionHow can this example be neutralized or renamed to another service than hoytsoft...? Pin
Krischu7-Nov-09 8:36
Krischu7-Nov-09 8:36 
GeneralRequestAdditionalTime Pin
ventcorp11-Aug-09 5:30
ventcorp11-Aug-09 5:30 
GeneralRe: RequestAdditionalTime Pin
David Hoyt12-Aug-09 17:07
David Hoyt12-Aug-09 17:07 
GeneralRe: RequestAdditionalTime Pin
David Hoyt13-Aug-09 6:03
David Hoyt13-Aug-09 6:03 
GeneralRe: RequestAdditionalTime Pin
David Hoyt13-Aug-09 8:39
David Hoyt13-Aug-09 8:39 
GeneralWebsite dead where the latest release is. Pin
SQLServerIO4-Mar-09 16:49
SQLServerIO4-Mar-09 16:49 
AnswerRe: Website dead where the latest release is. Pin
David Hoyt4-Mar-09 18:26
David Hoyt4-Mar-09 18:26 
GeneralRe: Website dead where the latest release is. Pin
David Hoyt4-Mar-09 18:30
David Hoyt4-Mar-09 18:30 
GeneralNEW RELEASE! [modified] Pin
David Hoyt11-Jul-08 16:50
David Hoyt11-Jul-08 16:50 
NewsRe: NEW RELEASE! Pin
David Hoyt12-Jul-08 21:22
David Hoyt12-Jul-08 21:22 
NewsServiceProcess invoked from another assembly Pin
pijy29-May-08 21:47
pijy29-May-08 21:47 
GeneralRe: ServiceProcess invoked from another assembly Pin
David Hoyt11-Jul-08 16:55
David Hoyt11-Jul-08 16:55 
AnswerRe: ServiceProcess invoked from another assembly Pin
DavidHoyt13-Jul-08 10:55
DavidHoyt13-Jul-08 10:55 
GeneralOnCustomCommand Pin
kim.david.hauser16-Aug-07 22:07
kim.david.hauser16-Aug-07 22:07 
AnswerRe: OnCustomCommand Pin
kim.david.hauser16-Aug-07 23:03
kim.david.hauser16-Aug-07 23:03 
GeneralRe: OnCustomCommand Pin
David Hoyt11-Jul-08 16:56
David Hoyt11-Jul-08 16:56 
QuestionProblem when running server with non System user account Pin
Tom Weilenmann7-Aug-07 4:59
Tom Weilenmann7-Aug-07 4:59 
AnswerRe: Problem when running server with non System user account Pin
David Hoyt11-Jul-08 16:59
David Hoyt11-Jul-08 16:59 
QuestionPossible to set CanStop attribute? Pin
MangeA4-Jun-07 9:48
MangeA4-Jun-07 9:48 
AnswerRe: Possible to set CanStop attribute? Pin
David Hoyt5-Jun-07 9:01
David Hoyt5-Jun-07 9:01 
AnswerRe: Possible to set CanStop attribute? Pin
David Hoyt11-Jul-08 17:01
David Hoyt11-Jul-08 17:01 
GeneralVista Compatible Pin
agates292-Apr-07 8:28
agates292-Apr-07 8:28 
GeneralRe: Vista Compatible Pin
David Hoyt11-Jul-08 17:02
David Hoyt11-Jul-08 17:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.