Click here to Skip to main content
Click here to Skip to main content

Self installing .NET service using the Win32 API

By , 28 Oct 2005
 

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:

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:

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)

About the Author

DavidHoyt
Software Developer (Senior) Lawrence Livermore National Laboratories
United States United States
Member
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!

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
AnswerRe: this.Log not working ?memberDavidHoyt13 Dec '05 - 21:01 
Hi Mark - 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!
GeneralRe: this.Log not working ?memberMark Longin14 Dec '05 - 4:09 
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.
 
Mark
AnswerRe: this.Log not working ?memberDavidHoyt14 Dec '05 - 6:24 
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:
 
-- modified at 12:26 Wednesday 14th December, 2005
GeneralRe: this.Log not working ?memberMark Longin14 Dec '05 - 9:18 
David,
 
Unfortunaltely, the registry key you are pointing me at does not contain a string value called Sources. Here is what it contains :
 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Services]
 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Services\HoytSoft Example Service]
"EventMessageFile"="C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\EventLogMessages.dll"
 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Services\Services]
"EventMessageFile"="C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\EventLogMessages.dll"

 
I tried removing the whole Services key with its two subkeys and their string values. That removed the Services entry from the Event Viewer. I then installed the service as compiled like you mention. I started, paused, resumed and stopped it, but saw no entries appearing in the Application log. However, it created a "HoytSoft Example Service" key under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application, but that didn't seem to be enough ...
 
I then unistalled the service, removed the created registry key and re-installed the original service. And ... SURPRISE ... the Services log was created again into the Event Log Viewer and contained all entries created before by the service compiled with the LogName = "Application" set ....
 
So, my problem remains. Hopefully you can still help me solving this because the service I need to write has to absolutely log into the Application log.
 
Thanks in advance,
 
Mark
GeneralRe: this.Log not working ?memberDavidHoyt14 Dec '05 - 14:18 
If doing it manually isn't working, you might try to do it programatically. (c: Check out: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdiagnosticseventlogclassdeleteeventsourcetopic.asp[^]
 
What version of Windows are you using, btw?
 
Thanks for your patience!
GeneralRe: this.Log not working ?memberMark Longin14 Dec '05 - 23:14 
David,
 
The result is the same, whether I remove it manually or programmatically.
 
My guess, without an indeep look into your code, is that, even with the LogName property set as you indicated, there are still some traces of a LogName = "Services" elsewhere in the code that force the use of a spooky event log. The entries made into that log only become visible in the event viewer after thruthfully creating the "Services" log.
 
I you can agree with this and find any solution to it, please post it. I maybe looking into it more deeply within a week or two.
 
PS : Development and testing is done on a XP Pro SP2 machine.
 
Regards,
 
Mark
NewsRe: this.Log not working ?memberCPP Basara26 Jun '06 - 0:02 
1.Before doing the following, make a change to ServiceAttribute.cs so that it support
'LogName' argument, then use like this : (This will then log into 'Application' log file)
  [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"
  )]
2.Find out 'Helper Methods' region ( In ServiceBase.cs ),
where checklog() function has a line
this.log.Source = this.displayName;
replace it with
this.log.Source = this.name;
( Because 'Source' should be service name.),
then it will work fine.
 
Just try it. It works.
 
If you want to use custom EventLog file, just read MSDN... it helpsSmile | :) .
No matter how you use this, make sure that EventLog file exist first,
then you will log any messages as you wish.
 
-----------------------------------------------------------------------
I'm interested in any kinds of computer programming, so are you, right?
GeneralErrormemberQuinton Viljoen21 Oct '05 - 0:46 
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.
 
Thanks
Quinton
AnswerRe: ErrormemberDavidHoyt21 Oct '05 - 10:47 
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!
GeneralRe: ErrormemberQuinton Viljoen23 Oct '05 - 18:25 
I will give it try using the new code.
 
Thanks.
GeneralRe: ErrormemberQuinton Viljoen27 Oct '05 - 1:20 
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.
 
Regards
Quinton
QuestionMalfunctionmemberSoykaf12 Oct '05 - 11:04 
Hi there!
 
First: Seems to be a great piece of code!
My problem is: I tried to use it (Windows XP Home SP2) and got nothing but an error.
When I first ran the service from within SharpDevelop, I got an error after some seconds (cause the service was running in the console-window). The service was installed anyway and I could also manipulate it from within the appropriate Manager (I have no idea how this one is called in English).
 
I then looked around in the source a bit...when I tried to run the .exe file in (bin/Debug) I get the following error:
 
"Application has genetated an exception that could not be handled.
 
Process id=0xe88, Thread id=0xedc (3804)"
 
and the app terminates.
 
What's the problem? (I do not have any idea about services in .NET at all...)
 
Greets - Soykaf
AnswerRe: MalfunctionmemberDavidHoyt12 Oct '05 - 13:24 
You were probably running the first version of the program. I maintain an updated version on my website: http://www.hoytsoft.org/serviceBase.aspx. Please download the latest version from there and your problem should be pretty much fixed.
 
So what's the cause of your error? Well, if it's as I expect, it's by design. In the first version, the program did not auto-detect if you're running it as a console app or as a service. You could switch the console version on or off by setting the "Debug" property in the Service attribute. It would look like:
 
using System;
using HoytSoft.Service.Attributes;
namespace MyServices {
    [Service(
    "HoytSoft_TestService", 
    ...
    <code>Debug = true</code>
    )]
    public class TestService : HoytSoft.Service.ServiceBase {
		...
    }
}
 
Setting it to true will skip the service setup procedure and execute your program directly as normal. If you tried to start it up without this flag set to true in Sharp Develop, it would set up all the service entries with the windows API and I believe it used to throw a ServiceStartupException to inform you that you're not running it in Debug mode.
 
Again, an upgrade to the latest version should avoid the problem. Best of luck!
 
-- modified at 19:25 Wednesday 12th October, 2005
QuestionCopyright NoticememberSteven J. Ackerman12 Oct '05 - 7:58 
Nice article. Sure seems to be a lot simpler than trying to use the Visual Studio service installer that Microsoft discusses. Thanks for sharing.
 
I noticed that the code posted here has a copyright notice in ServiceBase.cs, but the same code posted on your website doesn't. I was considering using your code to make it easier to install an IP address monitoring service that I had written, but now I'm unsure.
 
What are the terms of your copyright ?
 
Is the code posted here a derivative work of the code on your website ? Does it remain un-copyrighted ?
 
Is only this one file in the project protected by your copyright ?
 
Steven J. Ackerman, Consultant
ACS, Sarasota, FL
http://www.acscontrol.com
http://spaces.msn.com/members/sjackerman
steve@acscontrol.com

AnswerRe: Copyright NoticememberDavidHoyt12 Oct '05 - 8:59 
Thanks for your interest! Just a note - the code on the website is actually more up to date than the code project article. I've been meaning to update the code project's version for the past few days but haven't been able to. The latest version auto-detects if you're running it as a service or console program and runs it accordingly. Also, please be aware that since it's self-installing, it implies that the person running it has the proper security permissions to install a service.
 
As for the copyright notice, thanks for letting me know - I hadn't realized I'd left it out. Please preserve the copyright notice as seen in the code project article when using the code from hoytsoft.org. I will be sure to make that fix ASAP. Please also note the disclaimer to the left of every page on hoytsoft.org and abide by those rules.
 
You may use the code in any way you like, including modification, as long as the original notice is left intact. Any derivative work should include the original notice in its header or about box as appropriate.
 
ServiceBase.cs is the only copyrighted file since ServicesAPI.cs was mostly taken from another article that I can't seem to find. Good luck on your project and I'd be happy to know how it turns out for you! If you have any bug fixes, please post them here. Thank you!

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 28 Oct 2005
Article Copyright 2005 by DavidHoyt
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid