Simplest Window Service

I found a need for a class that could convert my app into a service without much of an effort. After going through many of the MSDN and CG articles, and lots of searching and postings in the VC++ discussion forum, I have come up with a simple, two-step, yet powerful Windows Service class called CSvcHandler.

A Little about Windows Services

Windows Services, also very well known as Windows NT services:

  • Enables us to create executables that run in their own Window sessions, not for minutes or hours, but for days and sometimes for months.
  • These services start (optionally) when the computer boots and can be stopped, paused, and restarted.
  • A service does not have any user interface; this makes services ideal for use on a server or whenever you need long-running functionality that does not interfere with other users who are working on the same computer.
  • Also, services can be run in the security context of a specific user account that is different from the logged-on user.

For more information check “Using services”(Platform SDK: DLLs, Processes, and Threads) in MSDN online documentation. Also, the winsvc.h header file contains a lot of information about structures, methods, and access types that are helpful in knowing more about the service internals.

After reading much about the services, and seeing lots of code, I still needed a simple, yet powerful service comprising of all features that I was in need of.

The Infrastructure(Main Constituents) of a Service

Following are the core functions that can make a service work, and are called when the service is controlled through Service Control Manager (SCM).

SERVICE_TABLE_ENTRY structure

This structure contains the information about the entry point(i.e. SvcMain()) that is used by SCM to modify the state of the service.

SvcMain()

The very well known ServiceMain function is the Entry point of the service for SCM.

SvcMain(DWORD argc, LPSTR* argv[])

All initialization tasks are done in the service’s SvcMain function when the service is started; it calls the RegisterServiceCtrlHandler().

SvcControlHandler()

Service Control Handler function, controls the pause/stop/restart/etc. state when requested through SCM, and is called by the dispatcher thread. It handles the control code passed in the dwOpcode parameter and then calls the SetServiceStatus() function to update the service status.

SvcCtrlHandler(DWORD dwOpcode)

Dispatch()

Used to dispatch the service control handler function, which is SvcMain in our case.

Dispatch calls the StartServiceCtrlDispatcher() function and connects the main thread of the service process to the service control manager, which causes the thread to be the service-control-dispatcher-thread for the calling process.

Service Extras

  • Install
  • Uninstall
  • Stop/shutdown
  • Pause
  • Resume/continue
  • Run/start service

I call these functions of a service extras because they are only needed when there is really a need to run the app through the console/command line. Usually users prefer mouse-clicks rather than typing-in a few words in the console. Therefore these methods are normally called when the user intends to change the status of the service from command line and not from SCM.

Interesting Facts

Service Display Name and Service Name

The string Service Display Name contains the display name to be used by user interface programs to identify the service. Modifiable in CSvcHandler constructor, while Service name is the string that specifies the name of the service to install in the SCM database. Modifiable in CSvcHandler constructor.

Problems can arise when service is uninstall/deleted using the service name and not by using service display name, so be careful about the two names. I had a tough time.

Also, almost all of the codes I have seen have initialized SERVICE_TABLE_ENTRY structure with two elements.

SERVICE_TABLE_ENTRY svcTable[] =
{
   { strSvcName, (LPSERVICE_MAIN_FUNCTION)SvcMain},
   { NULL, NULL}

};

But why do we always have to declare two elements, even though we are using only one? Why is the second element of the structure always NULL? It feels like we should add a “” to create a null terminated string, and here a null-terminated-service-table-entry. I still need to find out and if anyone of you knows about it, I’d be grateful to know.

What Do You Need to Do to Use the Class?

Step 1: Create an instance of CSvcHandler, and include header svchandler.h.

Step 2: Catch the command line parameters call the service methods.

#include "svchandler.h"
#include "winsvc.h"
int _tmain(int argc, TCHAR* argv[])
{
   CSvcHandler svc;
   if(argc == 2)
   {
      if(stricmp("install",  argv[1]) == 0) svc.Install();
      else if(stricmp("run", argv[1]) == 0)
      {
         svc.Run();
      }
      else if(stricmp("stop",      argv[1])== 0)  svc.Stop();
      else if(stricmp("pause",     argv[1])== 0)  svc.Pause();
      else if(stricmp("continue",  argv[1])== 0)  svc.Continue();
      else if(stricmp("uninstall", argv[1])== 0)  svc.Uninstall();
   }
   else
   {
      svc.Dispatch();
   }
}

Note: What if you would like to run the service from the Service Control Manager console rather than running it through command line? For that, you must install the service before beginning to use it through SCM, only Dispatch() wont help. Run the executable with “install” as a command line parameter, and then use start/stop from the service control manager.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read