Click here to Skip to main content
Email Password   helpLost your password?

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:

All of these should be fairly straightforward. Any questions?

Updates

Points of Interest

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionRunning a Process from within the service
Krischu
7:22 9 Nov '09  
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.



protected override void Start() {
this.Log("Service started");
ProcessStartInfo startInfo = new ProcessStartInfo();

startInfo.FileName = "c:\\test.bat";
startInfo.Arguments = " 12345";
startInfo.WorkingDirectory = @"C:\Temp";
startInfo.WindowStyle = ProcessWindowStyle.Maximized;
startInfo.ErrorDialog = true;

Process process;



process = Process.Start(startInfo);



if (process.WaitForExit(30000))
{
this.Log("Process terminated.");
}
else {
this.Log("Timed out waiting for process to end.");

}


}

GeneralHow can this example be neutralized or renamed to another service than hoytsoft...?
Krischu
9:36 7 Nov '09  
As far as I understand from the CPL license the code examples can be used freely in any environment, commercial and whatever. I would like to change all occurences of Hoytsoft to another "brand".

Of course I honor the effort of the original author and I also would not change any copyright mentionings (although I would like to see on the other hand, whether modifying the source code to the extent of totally changing everything, even the copyright entries, is possible from the CPL).

While I was changing every HoytSoft naming to another name I came to a point where suddenly ServiceAPI was unknown in the context. So maybe I overlooked something in the renaming of variables, methods and classes.


--
Christoph
GeneralRequestAdditionalTime
ventcorp
6:30 11 Aug '09  
The Microsoft ServiceBase class has a RequestAdditionalTime method to delay the Service Control Manager from timing out if it is taking a longer time to Stop (if finishing up some data processing before stop for example). Is there anything like that here? Thanks.
GeneralRe: RequestAdditionalTime
David Hoyt
18:07 12 Aug '09  
No I don't have anything like that. I'm unsure how they accomplish that since the time is set in the registry. The only way I can think of is to fudge it by reporting you've stopped even though you haven't really. If anyone knows of a SCM call to do this, I'd be happy to add it.

I believe 30 seconds is the default timeout - hopefully it's not taking more than 30 seconds to stop your service, though.

You could trick the SCM by fudging it and reporting you've stopped when you actually haven't. But I don't think it's a good idea.
GeneralRe: RequestAdditionalTime
David Hoyt
7:03 13 Aug '09  
You know, I might be wrong about that. I think there's a parameter (wait hint) in ReportSvcStatus that might allow us to do what you're talking about.
GeneralRe: RequestAdditionalTime
David Hoyt
9:39 13 Aug '09  
Adding it wasn't too difficult. I don't throw an exception like MS does -- I just silently ignore it.

All of this is in ServiceBase.cs. Change ReportSvcStatus() to:
private uint currentSvcState = Native.SERVICE_START_PENDING;
internal void ReportSvcStatus(uint CurrentState, uint Win32ExitCode, uint WaitHint) {
currentSvcState = CurrentState;

servStatus.dwCurrentState = CurrentState;
servStatus.dwWin32ExitCode = Win32ExitCode;
servStatus.dwWaitHint = WaitHint;

if (CurrentState == Native.SERVICE_START_PENDING)
servStatus.dwControlsAccepted = 0;
else servStatus.dwControlsAccepted = (uint)this.servControls;

if (CurrentState == Native.SERVICE_RUNNING || CurrentState == Native.SERVICE_STOPPED)
servStatus.dwCheckPoint = 0;
else servStatus.dwCheckPoint = checkPoint++;
Native.SetServiceStatus(this.servStatusHandle, ref servStatus);
}
And then add the following method:
public void RequestAdditionalTime(uint Time) {
if (debug)
return;

switch (currentSvcState) {
case Native.SERVICE_STOP_PENDING:
case Native.SERVICE_START_PENDING:
case Native.SERVICE_PAUSE_PENDING:
case Native.SERVICE_CONTINUE_PENDING:
break;
default:
return;
}
ReportSvcStatus(currentSvcState, Native.NO_ERROR, Time);
}

GeneralWebsite dead where the latest release is.
Burthold
17:49 4 Mar '09  
I'd LOVE to use this in my project but the latest version isn't available the website ip is there but there isn't a server dishing up the goodness!
AnswerRe: Website dead where the latest release is.
David Hoyt
19:26 4 Mar '09  
You can download it from codeplex as part of my asp.net suite: http://aspnetsuite.codeplex.com/[^]
GeneralRe: Website dead where the latest release is.
David Hoyt
19:30 4 Mar '09  
I host my site on a friend's server that unfortunately can go up and down a lot. That was just another reason to put the source on codeplex.
GeneralNEW RELEASE! [modified]
David Hoyt
17:50 11 Jul '08  
Hello all,

Thanks for everyone's support. I just released a NEW version on codeplex (http://aspnetsuite.codeplex.com/[^]) as part of my larger asp.net suite that includes proper UAC elevation prompts and compatibility with XP/Vista/Server 2003/Server 2008 and 32-bit and 64-bit editions.

It's released under a very liberal BSD-style license.

I pretty much gutted and re-did most of the library. I included some suggestions from the community (e.g. CustomMessage(...)). The install problem noted below should be solved. You can now literally create a service in 1 line of code (it probably wouldn't be very useful, though). So those who had Vista-compatibility problems, please try this version out. I also included a UAC library that I don't mind anyone using. It's backwards compatible with XP - but I haven't tested it on Windows 2000. I don't provide much documentation, unfortunately, but I hope it'll be mostly self-explanatory.

At any rate, check out the notes on that link[^], download it, and give 'er a go!

modified on Thursday, March 5, 2009 12:26 AM

NewsRe: NEW RELEASE!
David Hoyt
22:22 12 Jul '08  
I decided to change the license to the CPOL[^] as it seems to be less restrictive, yet offers more protection for the author. As always, now and in the future, please check the license that comes with the code. Big Grin
NewsServiceProcess invoked from another assembly
pijy
22:47 29 May '08  
Hello David and thanks for great code;)

I have some tip for everybody who would like to use HoytSoft.ServiceProcess in untypical circumstances.

If you want to invoke service installation from another executable - for example: from MSI installer by Installer class - your service will be installed properly but path to your executable in service properties will be '....\...\msiexec.exe' not 'YourPath\YourProg.exe', so, I made some changes in ServiceBase.cs in RunServices method.

To solve this issue you should replace this piece of code:
                                        
System.IO.FileInfo fi = new System.IO.FileInfo(envArgs[0]);
if (!s.baseInstall(fi.FullName, info.Name, ............
to:
  Assembly a = Assembly.GetAssembly(t);
if (!s.baseInstall(a.Location, info.Name, .............
ang get path for your executable from assembly passed by class type.
Remember, you must find and replace this code in two places in RunServices method.

For me it works fine Wink

Best regards
Pawel Jankowski
www.antyrama.pl[^]

p.s. sorry for my poor english Wink
GeneralRe: ServiceProcess invoked from another assembly
David Hoyt
17:55 11 Jul '08  
The latest version should fix this problem. If you're putting your service in an install, though, you'll want to add the "AutoInstall = false" named parameter to the service attribute so it won't try to automatically install it if it's not registered with the SCM. See my other post ("NEW RELEASE!") for more details.
AnswerRe: ServiceProcess invoked from another assembly
DavidHoyt
11:55 13 Jul '08  
This has gotten even easier - take a look at the new "Utilities" class. There are methods there for installing, uninstalling, starting, stopping, pausing, continuing, detecting if a service is running, etc.

Installing is as easy as:

Utilities.InstallService(typeof(MyService));
- or -
Utilities.InstallService("MyServiceNameHere");


Uninstalling:


Utilities.UninstallService(typeof(MyService));
- or -
Utilities.UninstallService("MyServiceNameHere");


It will automatically detect if UAC elevation is necessary and prompt as needed. Works in XP and Vista.
GeneralOnCustomCommand
kim.david.hauser
23:07 16 Aug '07  
Is it possible to catch the INT-Commands sent to this service with your class? (Implement OnCustomCommand?)

cheers kim

AnswerRe: OnCustomCommand
kim.david.hauser
0:03 17 Aug '07  
Haha and again - I answere my own question:

You can use the baseServiceControlHandler Function in the ServiceBase > All msgs go there > It's possible to forward all Messages >128 and <255 to a custom event / delegate > Works for me > Drop a line if you need the source.

Cheers & have a nice weekend Rose
Kim

GeneralRe: OnCustomCommand
David Hoyt
17:56 11 Jul '08  
The latest version addresses this and other issues. Please refer to my other post, "NEW RELEASE!"
QuestionProblem when running server with non System user account
Tom Weilenmann
5:59 7 Aug '07  
Hi

The service base class really does everything I want.

I only get a problem, when I try to run the service with a user account which is not the System account that is normaly used for services. We I try to start the service, I get the following error message:
Error 1053: The service did not respond to the start or control request in a
timely fashion

My service has to run with a specific account and password, so it can access files which are stored on a Linux box (with a Samba share).

Many thanks for all hints

Regards

Tom


AnswerRe: Problem when running server with non System user account
David Hoyt
17:59 11 Jul '08  
Try the latest version first. This is the one thing, though, that I didn't have time to add-on. But it would be easy enough, I think. Lookup the correct API calls and you could add it. Please post back any changes you make, though, so others can use it.

I would start by looking at the ChangeServiceConfig2() function (http://msdn.microsoft.com/en-us/library/ms681988(VS.85).aspx[^]). But I'm not sure about that.

Anyone have a better response?
QuestionPossible to set CanStop attribute?
MangeA
10:48 4 Jun '07  
What a great code! Runs w/o problems, even if an error msg is displayed if you are trying to stop the service from the control panel in Vista.

But, when creating a service from the VStudio GUI, there are several attributes that can be set for ther service at "design time". CanShutdown and CanStop among them. Is there a way to set CanStop=false to avoid users to abort my service, when using the HoytSoft code?

Kind regards
/Magnus in Sweden
AnswerRe: Possible to set CanStop attribute?
David Hoyt
10:01 5 Jun '07  
I'm not familiar with that, but I'm sure it could be done easily. I just got Vista working on my home computer and haven't had time to play with it. Please let me know if you find a solution and I'll post it on my website. Thanks so much! Glad you like it! Smile
AnswerRe: Possible to set CanStop attribute?
David Hoyt
18:01 11 Jul '08  
Try setting the ServiceControlType in the service attribute.
GeneralVista Compatible
agates29
9:28 2 Apr '07  
BTW, this code works great under XP. Under Vista, an error occurs whenever the service is stopped: Error 1061: The service cannot accept control message at this time. Any advice on fixing this error?

thanks again for the good work

jgates

GeneralRe: Vista Compatible
David Hoyt
18:02 11 Jul '08  
The latest version addresses this issue and it should be resolved. Please let me know if it persists. See my post "NEW RELEASE!" for more info.
GeneralOnCustomCommand ?
bacgeek
6:24 20 Oct '06  
Just wondering, is there a OnCustomCommand(int) Method exposed ? and if not can it be implemented

i have many services which use it for custome control of the services (like starting and stoping diffrent components of the service)



Last Updated 28 Oct 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010