News
 
Links
 
Disclaimer
The source code provided on this website is given "AS IS" and may be used in any application commercial or private. The author is not liable for any damages intentional or unintentional due to the use of this software. Use of the software acknowledges compliance with this disclaimer.

Support This Project
General: Self installing .NET service using Win32 API

Please see the latest notes for important update information!

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's 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 intricasies 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:

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 define an attribute:
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()
CustomMessage(uint OpCode)
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 execute 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()
    • TestCustomMessage()
    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 code project 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 2 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 it's 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 uesless. 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.

  • 07/11/08: Major changes! The library was rewritten and is now compatible with Windows XP, Vista, Server 2003, and Server 2008 32-bit and 64-bit editions! It is also UAC-enabled. Please refer to the source code to see the changes made. Although the above code will still work, check out the new examples in the source code. I've included a simplified service example that involves a few basic parameters and 2 delegates and that's it! It's disgustingly simple!

    Of note is the inclusion of a CPOL-style license meaning you are free to change, distribute, use, etc. all you want. Just include a simple acknowledgement somewhere, please! If anyone thinks that license is too restrictive, please send me an e-mail.

    Feel free to also use the UAC library all you like. You'll want to modify the HoytSoft.Common.UAC.Run executable with your own icon and then you'll want to sign it before distribution. If you do all of that, you'll get the nice UAC elevation prompt you see at the top of this article. Suh-weet!

    UAC Prompt

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)
Windows Programming
 
Questions or Comments
Availability by ???  10/5/2005
When will the article be available?

RE: Availability by David Hoyt  10/5/2005
It should be up by the end of today.

Self installing .NET service question by B A  10/19/2005
Thanks for sharing your excellent service library. It seems to work well for me, except that the ServiceCtrlHandler (baseServiceControlHandler) never seems to be called. Any ideas why this might be happening?

Thanks for your help!

RE: Self installing .NET service question by David Hoyt  10/20/2005
Thanks for using the code. (c: I ran into the same problem when I wasn’t signing my assembly w/ a strong name key. If you’re using the latest version (and I assume you are since you e-mailed me from the website instead of the code project article), you should be able to reference the dll and be good to go. If it’s still giving you trouble, try placing the HoytSoft.ServiceProcess code in your app directly. I have found that that usually solves the problem. I am still uncertain as to why the problem comes up at all and have posted a few questions to google groups, but have received no satisfactory answers. If you do find an answer, please let me know and I’ll make the changes and re-post the code. Thank you again! (c:

Error by Q V  10/21/2005
Hi

First of all, 5 out of 5!!! The service base is awesome! To bad about my debug attribute not working, as I thought that is quite nice to have, atleast still works when you build in VS. Whenever I try to pause or stop the service I get the following error. Could not pasuse the service on your local computer. The service did not return an error. This could be an internal Windows error or an internal service error. There's not much code in my servicer so far only logging to even viewer which is working. When I click stop a second time it finally stops the service, only after it times out while stopping, but does stop. Could you please lead me in the correct direction to resolve this issue.

RE: Error by David Hoyt  10/21/2005
Hi Quinton,

This looks like another problem with hosting the ServiceBase in a class library instead of the executable itself. I did some more testing and believe I have it fixed. The code project article does not have the latest updates since I did it today. Please go to http://www.hoytsoft.org/serviceBase.aspx[^] to download the latest version (revision 3) and see the changes I've made. Apparently a callback from the service API was not working correctly, but should be fixed now. The delegate I was using for the service control handler callback was going out of scope and therefore garbage collected and then not around when the SCM tried to use the callback. So it should work as expected now!

RE: Error by Q V  10/27/2005
Hi

I have been using the service now, and havn't had any issues. I am even executing unsafe code now within it and it's still 100% stable. I am using this service in a project. Its a TIF file comparing service, that monitors a folder compares signatures of images and splits a TIF accordingly. I am concidering writing my first article around this current project.

hi and help about self installing Services by A G  11/2/2005
Hi David,
I'm really impressed by your work you posted on the web on self installing services. I am curious if you have a translation of the program in VB.NET.. For instance an example program in VB.NET using the DLLs you have.

If you do can you please email me or let me know where I can find it??!

thanks

RE: hi and help about self installing Services by David Hoyt  11/2/2005
Hello there!

I'm sorry, but I do not currently have a version translated to VB.NET. You could either include the ServiceBase project in your solution or just reference the dll if you don't want to use the C# code. If you're talking about just the example, it should be fairly easy to do - just create your class, inherit from ServiceBase, add the Service attribute (in VB.NET, attributes are delimited by <>s intead of []s), and then override the appropriate methods and you're done!

If you do the translation, please let me know and I'd be glad to post the source code.

Question about your Self-Installing .Net Service by D C  11/16/2005
Hello, I recently discovered your self-installing .Net service information, and decided to use it in a project. This project is to monitor given performance counters stored in a database, and does so in 2 threads. One thread queries the performance counters every 15 seconds, and the other thread runs every 5 minutes and updates a table in the database that stores the resultant performance counter values.

The first problem that I'm seeing is that the service becomes unable to be stopped after approximately 30 seconds of execution. In the Services snap-in, the progress bar has to time out and manually kill the process. I added some event logging to detect when events were getting sent to the service; however, the "opcode" for "Stop" never got sent when I was seeing the problem. I don't know where the problem lies, but that is one of the symptoms.

The other problem that I was seeing is that the uninstall option often fails the first time, and sometimes works the second time, but this is not always the case. Sometimes it doesn't work at all. The if(!s.Uninstall()) which happens after the s.baseUninstall seems to fail fairly regularly, but often can be executed on the next execution.

The above 2 problems are the only ones I've been having, and was wondering if you'd seen them in the past, or if it was because I'm trying to use threads in the service.

RE: Question about your Self-Installing .Net Service by David Hoyt  11/16/2005
I ran into the same problem you're describing a few versions back because of the fact that I was using a delegate for a callback and then it was destroyed after the method returned (in ServiceBase.cs). The latest version should have fixed it. It could be a problem with your threads. I recently did a project just like you're describing using my class and had no problems (and it was fairly complex with lots and lots of threads running at the same time and synchronizing themselves - it was nuts). The problem may come from a thread that is blocking and not being aborted or not finishing. If a thread is blocking then Windows can't call baseServiceControlHandler(). Try running it as a background thread (although to be honest I ran all my threads as foreground threads w/o any problems).

As for the uninstall option - how are you using the uninstall? Was it from an uninstaller program or via the command line ("myservice.exe u")? I've only tested it from the command line and would be interested in any errors that occur when uninstalling from an installer package. Also, uninstall isn't called if the service doesn't exist - there's nothing to uninstall. And by uninstall, all it does is delete some SCM entries and then call the uninstall function. It might be intermittent if you uninstall then call it again without running the program normally or installing it. It should be fairly easy to modify the code to give you any desired behavior you're looking for. Let me know how it works out for you!

this.Log not working? by M L  12/13/2005
First of all after reading the article and looking at the source files : thanks for sharing this great code !

I downloaded the latest version (Rev3) from the hoytsoft.org website and compiled it in Release version on XP Pro with VS2003. I then ran the HoytSoft.Example.exe from the command line with the i argument to install it as a service. Using the Service Control Panel, I was able to start, pause, resume and stop the service.

However, the service did not write any entry at all in the Windows Event Log (I used the Windows Event Viewer to check it). Looking at the source code of the example service, I was expecting to see a lot of entries ...

What could I have done wrong ? Thanks for helping me out here !

RE: this.Log not working? by David Hoyt  12/14/2005
Thanks! (c: There could be problems with your event log - it could be full, in which case the code (depending on your point of view) gracefully fails to log anything. If it's not full, then check your logs for one named "Services" since all log entries are placed there (this is configurable in the Service attribute using the LogName property). If you changed the name of the log after already installing the service and generating log entries, then Windows can be finicky about it. It seems to prefer applications to keep using the same log every time. (It can be remedied through the registry, though). Honestly, I am not entirely sure what's causing you the log entry errors (it simply uses the normal .NET framework's logging classes). Have you stepped through the code and found the specific point at which it fails? That would sure help me to pinpoint the problem. (c: Thanks for your interest!

RE: this.Log not working? by M L  12/14/2005
David,

Thanks for your quick reaction !

"Services" !!! I was looking into the "Application" log. I didn't even know you could force the Event Log to create other logs then the tree basic ones. Learned something new here !

I tried to add the LogName property with as value "Application" to the example service code but at run time (installing the service), that generated an exception. Looking to the ServiceBase source code, I think I cannot just add the attribute, but I will have to at least add a constructing method which takes it as an argument. Isn't it ?

In any case, thanks again for sharing this great code and responding so quickly to my post.

RE: this.Log not working? by David Hoyt  12/14/2005
This is what I was referring to in my previous post about Windows being finicky - once an application is associated with a log, Windows doesn't like you changing it. The attribute works as-is when the app first writes a log entry. That is, the app associates itself with a specific log and then windows expects it to be the same every time. If it changes, it will throw you that exception. The solution is to edit the registry at:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Services
You will see a string value called "Sources" - delete that or delete the contents of it and then restart the service with the "Applications" log name. Your attribute declaration should look something like:

[Service(
"HoytSoft_ExampleService",
DisplayName = "HoytSoft Example Service",
Description = "Isn't this just absolutely amazing?",
ServiceType = ServiceType.Default,
ServiceAccessType = ServiceAccessType.AllAccess,
ServiceStartType = ServiceStartType.AutoStart,
ServiceErrorControl = ServiceErrorControl.Normal,
ServiceControls = ServiceControls.Default,
LogName = "Application"
)]
public class ExampleService : HoytSoft.ServiceProcess.ServiceBase {
...
}


After deleting the registry entry and re-running the service, you'll see log entries appear under "Application." If this was done from the very start (e.g. when first ran on a new system), you will not have any of these problems. Good luck - let me know how it turns out! (c: