Windows Service
Introduction
Have you ever wanted to create an windows service but can get it to work because
there's no way to troubleshoot your code?
Well I have the solution for you. We wont just make one project but three. A
class will contain all our important code and it will be statically linked to
two other projects, one that's the actual service that will be installed and one
that's a command line version for testing.
Let's get started. First lets create a Win32 console app. This will be used for
building the service. The code here will get replaced later.
Start up Visual Studio and on the menu go to File->New->Project.
In the new project pop up scroll down to Visual C++. Expand the tree if needed
and select Win32. In the Name box type WindowsServiceDemo and in the location
box put in where you want your project created at.

This wizard will start. Hit next. Uncheck Precompiled Header. Precompiled
headers cause more problems than they solve. Use them if you want, I won't be.
We don't want ALT or MFC so leave those unchecked. Since there is no GUI, we
don't want all the overhead that comes with MFC.

Hit Finish.
What you will see now is:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
Now lets create the second project for this solution. File->Add->New Project.
The new project wizard will start. Put WindowsServiceTester in for name and fill
in the location where you would like the project to be. I suggest you put it in
the same directory as the first project. The wizard will make subdirectories for
each project in the parent directory and this will keep all the projects for
this solution together.

Click OK. Hit next. Uncleck Pre-compiled headers. Getting Déjà vu? That's right
it has the same settings as the first project. There is a point to that but more
on that later.

Hit finish.
What you will see now is:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
Time to add the third project. This one is important because this is where all
the real code will be for your program. We are doing to create a class and
compile it into a static lib.
File->Add->New Project Name the project WindowsServiceClass and put it with the
rest of the projects for this solution. However this time select Win32 Project.
We want to make a lib not an exe.

Click OK. Click Next. Select Static library this time. Uncleck Pre-compiled
Headers.

Hit Finish.
In the solution explorer you should now see this.

You will notice that while it created a project there are no code files in it.
Lets fix that.
Right click on the WindowsServiceClass in the solution explorer select
Add->Class expand the tree if needed and select C++. Select C++ Class.

Click Add. In class name put CMyService. The other text boxes will populate
automatically.

Click Finish. A shell for your service class will be created for you.
MyService.h
#pragma once
class CMyService
{
public:
CMyService(void);
~CMyService(void);
};
MyService.cpp
#include "MyService.h"
CMyService::CMyService(void)
{
}
CMyService::~CMyService(void)
{
}
Let's put some code in it so we know it is being created and used
In MyService.cpp add a cout to the constructor so we will see it being created
in the test program.
CMyService::CMyService(void)
{
cout << "CMyService: Created\n";
}
If we are going to use cout then we need to include the header and tell the
compiler to use STD.
In MyService.h include iostream and std at the top of the file.
#pragma once
#include
using namespace std;
class CMyService
{
public:
CMyService(void);
~CMyService(void);
};
Now we need to use our class in the test program.
In the WindowsServiceTester project put #include "MyService.h" at the
top of the WindowsServiceTester.cpp file.
We need to create an instance of our class in order to use it. Add
CMyService aService; to the main function.
#include "stdafx.h"
#include "MyService.h"
int _tmain(int argc, _TCHAR* argv[])
{
CMyService aService;
return 0;
}
We still can't compile yet. Remember we are putting the class in a static lib so
we have to tell the compiler where it's located.
Right click on WindowsServiceTester and select properties. You will see the
Property Pages pop up. Change the All configurations. Expand the trees and
select Configuration Properties->C/C++->General and put ../WindowsServiceClass
in the Additional Include Directories Field. This will tell the compiler where
to find the header.

Hit Apply.
Now we need to tell the linker where to find the lib file. It's location will be
different for debug and release so we set them separately.
Change the Configuration to Debug. Expand Linker in the tree control and select
Input. Enter ../Debug/WindowsServiceClass.lib in the Additional Dependencies
field.

Hit Apply.
Change Configuration to Release. If not visible expand the Linker in the tree
control and select Input. Enter ../Release/WindowsServiceClass.lib in the
Additional Dependencies field.

Hit Apply.
Notice that we are using relative pathing. If you didn't put all the projects in
the same solution folder you will have to work out your paths as they will be
different than those shown here.
We wont be using unicode so we need to tell the compiler or it will get confused
later when we try and use char arrays and call API functions.
Right click on WindowsServieTester and select Properties. Change the
configuration to All Configurations. Expand Configuration Properties and select
General. Change Character Set to Not Set.

Now we are almost ready to compile we just need to tell the solution which
projects are dependant and which ones should start.
At the top of the solution explorer right click on WindowsServiceDemo and select
Project Dependencies.
On the Project Dependencies pop up select WindowsServiceTester from the drop
down selector. Check off WindowsServiceClass and WindowServiceDemo in the
Dependencies list.

This causes the build order to change so that the library is always built prior
to the exe. It will also build the service when ever we hit the compile button.
Next change the drop down to WindowsserviceDemo and make sure
WindowsServiceClass is checked off.

Switch to the build order tab and you should see this.

Now everything is being built in the right order.
Lets set the start up. Right click on the Solution WindowsServiceDemo at
the top of the Solution Explorer.

From the right click menu select Set Start up Projects. On the pop up select
Single Startup Project and choose WindowsServiceTester from the drop down.

You can hit the run button now if you want.

It won't do much but flash on the screen for a second. Lets create a small batch
file so we can pause the screen and see it. Go to the solution directory and
look for a folder in there called Debug. When you hit the run button it compile
the debug exe and placed it there. You should see WindowsServiceTester.exe.
Right click in the folder and choose New->Text Document. Change the name to
test.bat. Right click on test.bet and choose Edit. Put the following code in the
batch file.
WindowsServiceTester.exe
pause
Save the file.
Now double click on test.bat and it should run out test program and pause after
the execution so you can see the output.

You can see that the lib file was linked properly and the class was created in
our test program.
Now lets configure the other project to the same settings. This is key. The two
projects will be identical so that when something is wrong we know it is in the
service class code not in the shell code needed to make it a service. This way
we can troubleshoot service problems by running the service tester and checking
it's error messages.
Right click WindowsServiceDemo (the project name part way down not the solution
name at the top)

Select Properties from the right click menu.

Change configuration to All Configurations. Expand the C/C++ leaf and select
General.
In the Additional Include Directories enter ../WindowsServiceClass and hit
Apply.

Change the configuration to Debug. Expand the Linker leaf and select Input.

In additional Dependencies enter ../Debug/WindowsServiceClass.lib. Hit Apply.

Switch the configuration to Release. Select Input on the linker leaf if it is
not already selected. Enter ../Release/WindowsServiceClass.lib in the Additional
Depenencies field.

Hit Ok.
We wont be using unicode so we need to tell the compiler or it will get confused
later when we try and use char arrays and call API functions.
Right click on WindowsServieDemo and select Properties. Change the configuration
to All Configurations. Expand Configuration Properties and select General.
Change Character Set to Not Set.

Click Ok.
We need to tell the class project not to use unicode either.
Right click on WindowsServieClass and select Properties. Change the
configuration to All Configurations. Expand Configuration Properties and select
General. Change Character Set to Not Set.

Now the configurations should be the same. Lets test that.
Copy the code from WindowsServiceTester.cpp
// WindowsServiceTester.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "MyService.h"
int _tmain(int argc, _TCHAR* argv[])
{
CMyService aService;
return 0;
}
Replace the code in WindowsServiceDemo.cpp with the code you copied.
// WindowsServiceDemo.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "MyService.h"
int _tmain(int argc, _TCHAR* argv[])
{
CMyService aService;
return 0;
}
In order to test the configuration we need to temporarilly change the start up
project to the WindowsServiceDemo.
Right click WindowsServiceDemo Solution at the top of the Solution Explorer.

From the right click menu select Set Start up Projects. On the pop up select
Single Startup Project and choose WindowsServiceDemo from the drop down.

Click OK.
You can hit the run button now if you want.

It won't do much but flash on the screen for a second. Edit the test.bat file we
made earlier in the debug directory.
Add the line WindowsServiceDemo.exe just before the pause.
WindowsServiceTester.exe
WindowsServiceDemo.exe
pause
Save the batch file and then double click on it to run it.

You will see that they both work exactly the same. So we now have our paths and
links correct. We are going to change WindowsServiceDemo.exe into a service so
lets remove it from test.bat.
WindowsServiceTester.exe
pause
When we make a service it normally doesn't run once, it runs continuously once
started. This Service loop is where we will do work. since our service will be
looping we need a place in our class that does stuff each loop.
Switch to Class view in the Solution Explorer and expand WindowsServiceClass.

Right Click on CMyService select Add->Add Function. Enter void as the return
type and ServiceLoop as the function name. this function takes no parameters
because it is just a placeholder for code you want to execute over and over
until the service is asked to shut down.

Right now we are not going to put any code in there but this is where you would
put something like server waiting for incoming connection code, process next
packet or perhaps a named pipe waiting for a request. This might be a good place
to place timer code for those services that wait to perform an action until a
set time has come.
Click Finish.
Another thing our class needs is a place to put the start up code. This would be
were a network service would initialize sockets or a named pipe might be
created.
Right Click on CMyService select Add->Add Function. Enter bool as the return
type and StartMyService as the functions name. You may or may not take
parameters here for start up. I am not using any parameters for start up in this
example.

Click Finish.
In StartMyService change the code to return true. This will simulate a
successful start up. later you can add you initialization code and return true
or false depending if everything started up ok.
bool CMyService::StartMyService(void)
{
return true;
}
This last thing our class needs is a place to put code that runs when the
service is stopped. At this point you may be wondering why we don't use the
CMyService and ~CMyService for this. The reason is we want to separate errors
that might occur in the instantiation of the class from errors that occur from
starting and stopping sockets, pipes and the like.
Right Click on CMyService select Add->Add Function. Enter void for the return
type and StopMyService for the function name.

Click Finish.
The service will have it's own loop that waits for a service stop message from
the OS, in the Service tester we need to simulate this loop and it will stop
when we hit a key in the testers console window.
In WindowsServiceTester.cpp add the header line #include <conio.h>. Put a while
loop in that waits for a keypress to end.
// WindowsServiceTester.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "MyService.h"
#include <conio.h>
int _tmain(int argc, _TCHAR* argv[])
{
CMyService aService;
if(aService.StartMyService())
{
printf("Press any key to stop.\n");
while(_kbhit() == false)
{
aService.ServiceLoop();
}
aService.StopMyService();
printf("Service closed successfully.\n");
}
return 0;
}
Before we can test this we need to switch our start up project back to
WindowsServiceTester. Lets do it a different way this time. Right click on
WindowsServiceTester in the Solution Explorer and select Set as Startup Project.

Why didn't we do it this way before you might ask. I wanted to show you the pop
up selector because it has more options and lets you set multiple projects to
start up. Now that you've learned that we can take a few short cuts when we only
need one project set to start up.
You can hit the run button now if you want.

You should see.

We don't need the test.bat file now since it stays on the screen looping until
we hit a key.
Now lets change WindowsServiceDemo into a real service. The code for a service
shell is well known and available in many locations. Here it is.
In WindowsServiceDemo.cpp change the code to:
// WindowsServiceDemo.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include "MyService.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Here is where we name our service. don't use spaces in the name. If you use spaces and the name is long the service gets jacked up. Welcome to Microsoft
TCHAR* gszServiceName = TEXT("MyService");
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE ServiceControlEvent = 0;
void WINAPI ServiceControlHandler( DWORD controlCode )
{
switch ( controlCode )
{
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
SetEvent( ServiceControlEvent );
return;
case SERVICE_CONTROL_PAUSE:
break;
case SERVICE_CONTROL_CONTINUE:
break;
default:
if ( controlCode >= 128 && controlCode <= 255 )
// user defined control code
break;
else
// unrecognised control code
break;
}
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
void WINAPI ServiceMain( DWORD /*argc*/, TCHAR* /*argv*/[] )
{
// initialise service status
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = RegisterServiceCtrlHandler( gszServiceName, ServiceControlHandler );
if ( serviceStatusHandle )
{
// service is starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// Create the Controlling Event here
ServiceControlEvent = CreateEvent( 0, FALSE, FALSE, 0 );
// Service running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
//////////////////////////////////////////////////////////////////////////////////////////////
//This is the only area you need to be concerned with. This is where the service loop is and
//this is where you will put your class and run its loop
CMyService aService;
if(aService.StartMyService())
{
do
{
aService.ServiceLoop();
}
while ( WaitForSingleObject( ServiceControlEvent, 30 ) == WAIT_TIMEOUT );
aService.StopMyService();
}
//Everything else is just boilerplate needed for a service.
///////////////////////////////////////////////////////////////////////////////////////////////
// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do cleanup here
CloseHandle( ServiceControlEvent );
ServiceControlEvent = 0;
// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}
void RunService()
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ gszServiceName, ServiceMain },
{ 0, 0 }
};
StartServiceCtrlDispatcher( serviceTable );
}
void InstallService()
{
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0,
SC_MANAGER_CREATE_SERVICE );
if ( serviceControlManager )
{
char path[ _MAX_PATH + 1 ];
if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 )
{
SC_HANDLE service = CreateService( serviceControlManager,
gszServiceName, gszServiceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
0, 0, 0, 0, 0 );
if ( service )
{
CloseServiceHandle( service );
printf("%s Installed Successfully\n", gszServiceName);
}
else
{
if(GetLastError() == ERROR_SERVICE_EXISTS)
printf("%s Already Exists.\n", gszServiceName);
else
printf("%s Was not Installed Successfully. Error Code %d\n", gszServiceName, GetLastError());
}
}
CloseServiceHandle( serviceControlManager );
}
}
void UninstallService()
{
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0,
SC_MANAGER_CONNECT );
if ( serviceControlManager )
{
SC_HANDLE service = OpenService( serviceControlManager,
gszServiceName, SERVICE_QUERY_STATUS | DELETE );
if ( service )
{
SERVICE_STATUS serviceStatus;
if ( QueryServiceStatus( service, &serviceStatus ) )
{
if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
{
if(DeleteService( service ))
printf("%s Removed Successfully\n", gszServiceName);
else
{
DWORD dwError;
dwError = GetLastError();
if(dwError == ERROR_ACCESS_DENIED)
printf("Access Denied While trying to Remove %s \n", gszServiceName);
else if(dwError == ERROR_INVALID_HANDLE)
printf("Handle invalid while trying to Remove %s \n", gszServiceName);
else if(dwError == ERROR_SERVICE_MARKED_FOR_DELETE)
printf("%s already marked for deletion\n", gszServiceName);
}
}
else
{
printf("%s is still Running.\n", gszServiceName);
}
}
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}
int _tmain( int argc, TCHAR* argv[] )
{
if ( argc > 1 && lstrcmpi( argv[1], TEXT("install") ) == 0 )
{
InstallService();
}
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 )
{
UninstallService();
}
else
{
RunService();
}
return 0;
}
Hit the run button now. It will compile the service.

Let's test installing and uninstalling the service. For this navigate to the
Debug directory. Remember, it's where we created the test.bat. We could just
type our commands into the prompt but then we have to navigate tot eh right
directory in command prompt and type the same commands over and over when
testing. Making batch files for this makes it a lot easier.
Not create two batch files. One named install.bat and the other named
uninstall.bat. I bet you can guess what these will do.
Install.bat
"C:\Public\work\Tutorials\WindowsServiceDemo\Debug\WindowsServiceDemo.exe" install
pause
Uninstall.bat
"C:\Public\work\Tutorials\WindowsServiceDemo\Debug\WindowsServiceDemo.exe" uninstall
pause
You need to specify the complete path because the service registration runs from
the windows system32 directory and will need to know the path of the service exe
in order to install it.
Of course, these show the path of WindowsServiceDemo.exe on my pc. You will need
to modify them to reflect where you put your project files. Also when you are
actually doing final tests you will want to compile and install the Release
version of the WindowsServiceDemo.exe
If you are running Windows 7 or Vista you will need to right click on the
install.bat and choose Run as Administrator in order to install a service.

Now lets check services and see if its there.

There it is. Congratulations you have made your first windows service. Now lets
remove it by right clicking and running as administrator the uninstall.bat.

Now it's gone.

You can now add what ever code you want to the CMyService class and run it
either in a testing command line or as an installed service. Later you may want
to look into posting messages to the windows log from your service. While during
development trying to use logs for traces is far to slow for day to day
programming it is useful when things go wrong on a clients machine to be able to
have them send you a copy of the system log with messages from your service in
it.
I hope you found this useful.