Click here to Skip to main content
12,820,283 members (29,331 online)
Click here to Skip to main content
Add your own
alternative version


250 bookmarked
Posted 29 Dec 2001

Beginner's introductory guide to writing, installing, starting, stopping NT services

, 29 Dec 2001 CPOL
Rate this:
Please Sign up or sign in to vote.
Includes a generic skeleton for a simple NT service. Explains how to install, start, and stop the service programmatically.

Introduction to NT Services

At system boot, Windows NT/2K starts an RPC server called as the Service Control Manager (SCM). An NT service is basically a win32 program that is loaded by the SCM. They are loaded before any user has logged into the system. Services may sometimes be manually started instead of getting started automatically at boot time. It was quite recently that I made my first attempt at writing an NT service and I found to my chagrin that very little information was available for a service-newbie. Even on Code Project I could only find wrapper classes which was not what I wanted. 

This article gives you a generic service skeleton which you can use as a starting point when you write your first service. The service does nothing basically. I found a couple of examples on the net both of which were called beeper services because that's what they did. They beeped the system speaker at regular intervals. I thought I'd do the same for my skeleton service because that seems to be the easiest way to give an indication that the service is up and running.

Service Skeleton

The main function

I have written my service as a console application and therefore the main function. But I presume there is nothing stopping you from writing your service as a GUI application with a WinMain but since I haven't tried it out yet, I won't delve too much into that. All that the main function does is to call StartServiceCtrlDispatcher to connect the main thread of our service to the SCM. We simply fill up the SERVICE_TABLE_ENTRY structure and call StartServiceCtrlDispatcher passing the SERVICE_TABLE_ENTRY structure as a parameter.

<PRE lang=c++>SERVICE_TABLE_ENTRY servicetable[]= { {strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL,NULL} };

strServiceName is the name of our service. We also pass a pointer to our ServiceMain function. I used the name ServiceMain thinking that, that was mandatory, but later on I realized that you can use any name you want to use. Rather silly of me to think so, I guess. Members of the final entry in the table must have NULL values as an indication that this is the end of the table.

<PRE lang=c++>StartServiceCtrlDispatcher(servicetable);

Calling StartServiceCtrlDispatcher is a straightforward thing as you can see. Simply pass a pointer to a SERVICE_TABLE_ENTRY array. If StartServiceCtrlDispatcher fails it returns false immediately, otherwise it will return only after our service has terminated. Very recently I have understood that the same executable can have more than one service, but again since I didn't really try it out, I will refrain from making any bold statements. Anyway I think that one service per exe is a smart way of doing things which follows the keep-it-simple paradigm.

The ServiceMain function

The ServiceMain is the entry point function for our service. When the SCM starts our service it creates a new thread for executing our ServiceMain function. The first thing a ServiceMain does is to call RegisterServiceCtrlHandler to register a handler function. The service uses this handler function as it's control handler function which receives control codes including codes to start, stop, pause and continue the service.

<PRE lang=c++>RegisterServiceCtrlHandler(strServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);

Once we have registered our service control handler, we need to update the SCM with regard to our service's status. We can do this using the SetServiceStatus API call. We will need to do this several times during the course of our program and each time it involves filling up a SERVICE_STATUS structure. Therefore I have written a function called UpdateServiceStatus which will automate this for us. I discuss this function later on in this article. Basically what we do after registering our handler is to update the SCM with the SERVICE_START_PENDING status for our service, which means that our service is starting.

<PRE lang=c++>UpdateServiceStatus(SERVICE_START_PENDING,NO_ERROR,0,1,3000);

3000 is the dwWaitHint parameter of the SERVICE_STATUS structure which is in milliseconds. If this time has expired and the service status has not yet changed, the SCM assumes that an error has occured. Once we have updated the SCM with our status, we create an event. We do this so that we can use WaitForSingleObject on this event. We can then set the event to terminate our service somewhere else in our program.

<PRE lang=c++>killServiceEvent=CreateEvent(0,TRUE,FALSE,0);

After we do this we call UpdateServiceStatus again with the SERVICE_START_PENDING status, only this time we increment the dwCheckPoint parameter of the SERVICE_STATUS structure. This parameter is used to track the progress of a service during a lengthy start or stop operation. Now we start our service execution thread.

<PRE lang=c++>StartServiceThread();

I discuss this function later on, but in summary it simply starts a new thread using CreateThread where we put our actual functionality. Now we call UpdateServiceStatus again, passing SERVICE_RUNNING as our parameter.

<PRE lang=c++>UpdateServiceStatus(SERVICE_RUNNING,NO_ERROR,0,0,0);

Well, now that our service is up and running we need to call WaitForSingleObject on the event we created earlier. Because ServiceMain should not finish till our service has terminated. Again this is a very simple step as shown below.

<PRE lang=c++>WaitForSingleObject(killServiceEvent,INFINITE);

The UpdateServiceStatus function

As I had mentioned earlier, I wrote this function to wrap the SetServiceStatus API call. It is by no means an innovative idea. Just about every example of a service that I saw on the web used some form of a wrapper function, because during the course of a service program, we need to change the service status several times. Basically what we do in this function is to populate a SERVICE_STATUS structure. I'll mention some of the members of this structure that are important to us. I strongly suggest that you look up this structure in your copy of MSDN.

dwCurrentState :- This indicates the current state of the service. Some of the values we use are SERVICE_STOPPED, SERVICE_RUNNING and SERVICE_START_PENDING. You can look up the other allowed values on MSDN.

dwControlsAccepted :- This is used to indicate the control codes that will be handled by our service handler. For our skeleton service I have used SERVICE_ACCEPT_STOP and SERVICE_ACCEPT_SHUTDOWN. These are the only two control codes our skeleton service will handle. When our service is in the SERVICE_START_PENDING state we must set this parameter to zero.

dwCheckPoint :- I have mentioned about this parameter earlier. The service increments this value during a lengthy start or stop operation. Any program that invokes an operation on the service can use this value to track the progress of various operations. If you are wondering how a program can do that, take a look at the QueryServiceStatus API call.

dwWaitHint :- This specifies the interval in milliseconds before the service status changes again. If the service status has not changed by then, the SCM assumes that an error has occured. Use zero for this parameter when we are setting the service status to SERVICE_RUNNING.

<PRE lang=c++>SetServiceStatus(nServiceStatusHandle,&nServiceStatus);

The first parameter we pass is the service status handle which is returned by the RegisterServiceCtrlHandler function. I have saved this in a global variable. The second parameter is a pointer to the SERVICE_STATUS structure that we have populated.

The StartServiceThread function

Well, this function simply starts our service execution thread using the CreateThread API call. If the thread is created successfully I also set the global nServiceRunning variable to true. I have used CreateThread but you might want to use _beginthreadex if you are planning on using some of the CRT functions in your thread.

The ServiceExecutionThread function

This function is our main service execution thread. In our skeleton service I have simply put a while loop using my global nServiceRunning BOOL variable as the while's evaluation expression. Thus till nServiceRunning is made false the while loop will loop endlessly. Please keep in mind that an endless while loop will use up your CPU infinitely till your machine crawls to a pathetic frozen state. I am avoiding this using a Sleep, but you might want to use some kind of blocking calls or waiting calls in your programs.

<PRE lang=c++>while(nServiceRunning) { Beep(450,150); Sleep(4000); }

Well, that's the service body for you. It won't get any simpler than that I guess. Of course the functionality is useless, that's why we call it a skeleton service.

The ServiceCtrlHandler function

This is our service's control handler function. All service control requests like starting a service, stopping a service etc. are handled by the control handler. The MSDN prototype for this function is as follows.

<PRE lang=c++>VOID WINAPI Handler( DWORD fdwControl // requested control code );

Basically we put a switch statement on the fdwControl variable and we have case blocks for each control code that we intend to handle.

<PRE lang=c++>switch(nControlCode) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: nServiceCurrentStatus=SERVICE_STOP_PENDING; success=UpdateServiceStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000); KillService(); return; default: break; }

As you can see our skeleton program's switch construct handles only two control codes, SERVICE_CONTROL_SHUTDOWN and SERVICE_CONTROL_STOP. If you scroll up, you'll see that when I set the service status to SERVICE_RUNNING I set the dwControlsAccepted member of the SERVICE_STATUS structure to SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN. Thus these are the only two control codes that the SCM will send to our control handler function. As you can see, for both cases, we are using the same code. All we do is to change our global service status variable to SERVICE_STOP_PENDING and then we call UpdateServiceStatus passing SERVICE_STOP_PENDING. Then we call our own KillService function (which I explain down below) and return.

The KillService function

Well, we use the KillService function to terminate our service. We first set nServiceRunning to false, so that our service execution thread exits. Then we set our blocking event so that ServiceMain will exit.

<PRE lang=c++>nServiceRunning=false; SetEvent(killServiceEvent);

Once we have done that we need to inform the SCM that our service has terminated. So we call UpdateServiceStatus passing SERVICE_STOPPED as the dwCurrentState parameter.

<PRE lang=c++>UpdateServiceStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);

Installing our service

First we use the API call OpenSCManager to get a handle to the SCM database.

<PRE lang=c++>scm=OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);

We pass 0 for both lpMachineName and lpDatabaseName as we need to open the SCM database on the local machine. We pass SC_MANAGER_CREATE_SERVICE as our dwDesiredAccess so that we can use the CreateService API call to create our new service and add it to the SCM database.

<PRE lang=c++>CreateService(scm,"NishService", "Buster's first NT service", SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "D:\\nish\\FirstService\\Debug\\FirstService.exe", 0,0,0,0,0);

You must look up CreateService on MSDN. I have used SERVICE_ALL_ACCESS as my dwDesiredAccess parameter. This allows me full rights and I can do as I please. I have used SERVICE_DEMAND_START as the dwStartType parameter. This means the service won't start automatically at system boot time. It will need to be manually started either via the control-panel's Component Services applet or programmatically using StartService. Later on in this article I show you how to programmatically start and stop our service. You need to specify the full path of the service executable. The last 5 parameters can be ignored for now. To be frank, as soon as I found that they can all be NULL, I didn't bother to interpret their purpose. But I suggest that you go ahead and figure out how you can put them to proper use.

Starting our service programmatically

The first thing we need to do is to use OpenSCManager to obtain a handle to the SCM. Now we use OpenService to obtain a handle to our skeleton service.

<PRE lang=c++>NishService=OpenService(scm,"NishService",SERVICE_ALL_ACCESS);

If OpenService successfully returns,( which we can figure out by checking whether it has returned NULL, in which case the call has failed), we can proceed by calling StartService using the handle returned by OpenService.

<PRE lang=c++>StartService(NishService,0,NULL);

Since we are not passing any parameters to ServiceMain, I am passing 0 and NULL as the 2nd and 3rd parameters. If StartService succeeds, the return value is nonzero and we can assume that our service has been successfully started. This will become obvious soon, when the PC speaker starts beeping once in 4 seconds and you might get rude stares from your co-employees. In which case you might want to stop the service.

Stopping our service programmatically

The first two steps for stopping our service are same as for starting our service. We call OpenSCManager  to get a handle to the SCM and then call OpenService to get a handle to our service. Now we can use the ControlService API call to send a control code to our service handler.

<PRE lang=c++>ControlService(NishService,SERVICE_CONTROL_STOP,&m_SERVICE_STATUS);

m_SERVICE_STATUS is a SERVICE_STATUS structure which will receive status information about our service. As you can see I have passed SERVICE_CONTROL_STOP as the control code. And we know how we have handled this control code in our service handler. Now it all begins to fit into a pattern, huh? By now the 4-second beeping will have stopped and the rude stares will slowly fade away.

Thank You [and remember that I am a service-newbie too]


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


About the Author

Nish Nishant
United States United States
Nish Nishant is the Principal Software Architect/Consultant for Ganymede Software Solutions LLC, and is based out of Columbus, Ohio. He has over 17 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish was a Microsoft Visual C++ MVP between 2002 and 2015.

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : If you are interested in hiring Nish as a consultant, you can reach him via his google email id voidnish.

Company Website :

You may also be interested in...


Comments and Discussions

GeneralOpenSCManager( ) return null on non-admin user account Pin
Waqar Abbas18-Jul-10 23:17
memberWaqar Abbas18-Jul-10 23:17 
GeneralRe: OpenSCManager( ) return null on non-admin user account Pin
Member 425712116-Nov-10 19:28
memberMember 425712116-Nov-10 19:28 
Generalgood overview Pin
Donsw29-Nov-09 10:15
memberDonsw29-Nov-09 10:15 
QuestionRepeatedly getting SetServiceStatus 6 in Application log Pin
vinutha_8417-May-09 19:24
membervinutha_8417-May-09 19:24 
GeneralGood example Pin
Sharath C V25-Apr-09 10:02
memberSharath C V25-Apr-09 10:02 
QuestionStartService() fails with The system cannot find the file specified. or (on some machines)Logon failure: unknown user name or bad password . Pin
roshan_nikam8-Apr-09 0:13
memberroshan_nikam8-Apr-09 0:13 
AnswerRe: StartService() fails with The system cannot find the file specified. or (on some machines)Logon failure: unknown user name or bad password . Pin
fazorin18-Sep-09 5:30
memberfazorin18-Sep-09 5:30 
QuestionAlways got error (1063) in StartServiceCtrlDispatcher Pin
arifliminto8619-Jan-09 14:39
memberarifliminto8619-Jan-09 14:39 
AnswerRe: Always got error (1063) in StartServiceCtrlDispatcher Pin
DarthVona4-Mar-09 4:18
memberDarthVona4-Mar-09 4:18 
GeneralStartServiceCtrlDispatcher returns false Pin
Moorthi N24-Oct-08 11:42
memberMoorthi N24-Oct-08 11:42 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170308.1 | Last Updated 30 Dec 2001
Article Copyright 2001 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid