SFL 2.0: Service Framework Library for Native Windows Service Applications, Part 1

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Writing Windows services never was an easy thing, and it remains quite rarely explained in contemporary technical programming manuals, compared to other types of Windows applications in spite the fact that the family of Windows NT-based operation systems already has a long history at the moment. A more confusing thing is that this complex but really interesting type of Windows application has not been honored with its own object-oriented library from Microsoft. Of course, ATL service cannot be considered the one, being merely a very specific type of COM server but not a sort of general solution for Windows services.

The library proposed here seems like an attempt to change the situation, at least partially, and close the gap a bit. The library, of course, looks very brief and modest, with no doubt. Nevertheless, I hope it will encourage other developers and possibly draw their attention to further improvement.

This hope is sustained by reaction to the framework version 1.0. The latter was introduced to Russian programmers’ community (at www.rsdn.ru, the Russian Software Developers Network) in 2004. Publication of that version had shown some interest was paid to the topic, and that fact had convinced me the library requires further development efforts.

Articles

The articles unveil the details and abilities of SFL usage in sequential manner. They will approach the most complex issues at the very end.

  • Part I: The minimal application is dissected to let you understand the general structure of an SFL application. This application implements the most basic type of native Windows service . Windows NT-style service.
  • Part II: The extended service type implementation is described there. This service is the Windows 2000 style.
  • Part III: The SFL architecture is described in the article, showing the library class hierarchy and giving a description of framework entry points.
  • Part IV: Additional abilities of service implementation are explained in the final article. It shows how to implement a multi-service application; service clone creation is presented there as well. This part also includes a description for supplementary classes intended for controlling the services.

Minimal SFL Application

Here’s the full C++ code for a Windows NT-style service.

Note: The particular service style to be implemented depends on a version of the service control map. The SFL_BEGIN_CONTROL_MAP macro corresponds to the service to be used with Windows NT versions not greater than 4.0. To implement a service of a newer (Windows 2000/XP/2003) style, the SFL_BEGIN_CONTROL_MAP_EX macro must be used (see Part II).

Note: To understand the difference between service styles, please refer to MSDN articles, HandlerEx and RegisterServiceCtrlHandlerEx in particular.

#include "stdafx.h"
#include "SflBase.h"


class CMyService:
   public CServiceBaseT<CMyService,
   SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_PAUSE_CONTINUE>
{
   SFL_DECLARE_SERVICECLASS_FRIENDS(CMyService)

   SFL_BEGIN_CONTROL_MAP(CMyService)
      SFL_HANDLE_CONTROL_STOP()
      SFL_HANDLE_CONTROL_PAUSE()
      SFL_HANDLE_CONTROL_CONTINUE()
   SFL_END_CONTROL_MAP()

   DWORD OnStop(DWORD& /*dwWin32Err*/, DWORD& /*dwSpecificErr*/,
                BOOL& bHandled)
   {
      bHandled = TRUE;
      return SERVICE_STOPPED;
   }

   DWORD OnPause(DWORD& /*dwWin32Err*/, DWORD& /*dwSpecificErr*/,
                 BOOL& bHandled)
   {
      bHandled = TRUE;
      return SERVICE_PAUSED;
   }
   DWORD OnContinue(DWORD& /*dwWin32Err*/, DWORD& /*dwSpecificErr*/,
                    BOOL& bHandled)
   {
      bHandled = TRUE;
     return SERVICE_RUNNING;
   }
};

SFL_BEGIN_SERVICE_MAP(CSimpleServiceApp)
   SFL_SERVICE_ENTRY2(CMyService, 0, "My Service")
SFL_END_SERVICE_MAP()

This code has been compiled and linked as a regular Windows console application (please make sure you read and remember that, because it’s the key point for SFL code), it gives you the simplest service executable module. Although this service does nothing useful, it’s a full Windows service that is able to be run, paused, continued, and stopped (being registered properly, of course, but the issues of service registration and un-registration are beyond this article’s scope).

It’s easy to see that the whole application is made of a couple of classes and three map declarations. In the aspect of usage of class templates and maps, SFL is similar to ATL.

Header Files Inclusion

The SflBase.h file is mandatory for an SFL application and must either be included explicitly into a compiled C++ unit or implicitly contained by stdafx.h (precompiled header). Please, in any case, make sure the stdafx.h includes windows.h to let your application compile successfully.

Service Class Declaration

Your service class must be derived from the template class CServiceBaseT<>, which implements the main Windows service logic and corresponding behavior. Your class typically implements only control event handlers, such as OnStop(), OnPause(), and OnContinue() in this sample, and the framework takes care of all other aspects related to interaction between the service and SCM.

The core of any SFL-based service class is a service control map. This is the place where you define what control codes will be processed by the service. In fact, the control map in whole implements a service Handler(Ex) function.

Note: And based precisely on this latter point, this is the place where you declare the style of service—Windows NT or Windows 2000—to be implemented. This example, as you may remember, implements the basic Windows NT style because the SFL_BEGIN_CONTROL_MAP macro implements an old-style Handler version of a control handler function.

The map consists of a number of SFL_HANDLE_CONTROL_XXX macros within the SFL_BEGIN_CONTROL_MAP and SFL_END_CONTROL_MAP macro braces. Of course, you’ve met this coding style before, in MFC or ATL projects at least.

The list of system control codes accepted by your service is defined by the second template parameter passed to the base class. In this sample, it’s a combination of SERVICE_ACCEPT_STOP and SERVICE_ACCEPT_PAUSE_CONTINUE.

Once you declare that your service accepts the Stop and Pause/Continue controls, the corresponding codes will be passed from SCM to the service for handling. As you may see, the service contains appropriate control entries in its control map.

Note: Although this sample does not implement handling for custom control codes, I’ll give you a hint on how you handle these: The code in range 128 to 255 must be processed with the entry:

SFL_HANDLE_CONTROL(<control code>, <handler name>)

The control map binds the control codes to appropriate handlers. Each handler has the same prototype:

DWORD (CMyService::*)(DWORD& dwWin32Err, DWORD& dwSpecificErr,
                      BOOL& bHandled);

The dwWin32Err parameter is intended to pass a Windows error code back to a framework in case some Windows API critical error happens during control code handling.

In case some custom service logic is broken during control code handling, you can pass the error code specific for your service via dwSpecificErr. The dwWin32Err code is ignored by the framework in case the dwSpecificErr is not zero.

Note: Remember, the error codes (both Win32 and service-specific) matter only in case of service stopping. Under any other circumstances, they are ignored by the system.

The code returned by the handler will be interpreted as the service state code to be set for your service in case the bHandled variable was set to TRUE at the moment of return from the control handling code.

Service Map Declaration

The service map ties together the service application class and the service class. Here is a simple service application class declaration (that could be found in SflBase.h):

namespace SFL {
   typedef class _TSimpleApp: public CServiceAppT<_TSimpleApp>
   {
   } CSimpleServiceApp;
}

This implies that the application class doesn’t require any additional functionality other than the one provided by the base application class template.

Note: Of course, your application can implement richer behavior. In this case, you must pass your specific application class name instead of CSimpleServiceApp. The technique of extending of application class will be explained later, in Part IV.

To specify your intention to include your service class implementation in the current service application, you place the SFL_SERVICE_ENTRYxxx macro between the SFL_BEGIN_SERVICE_MAP and SFL_END_SERVICE_MAP macro braces. In this particular case, your entry looks like this:

SFL_SERVICE_ENTRY2(CMyService, 0, "My Service")

As you can understand, the entry states that your service must be identified as “My Service” in the system service list.

Well, that’s all with the application definition. Now, you can build your project.

Some Notes on Service Installation

You have built your service application. No doubt, you have a strong desire to make sure it works. But, it is still not registered as a service in your system, and you can see nothing that could help you with this, right?

Okay, I dare to be frank with you. This primitive service application does not deserve its own installation code. The great thing is that you never need anything but a special utility from Microsoft, called SC.EXE, to install and delete your service application. The command line doing these actions may look as follows:

sc.exe create "My Service" binPath= "<full_app_path>"
sc.exe delete "My Service"

Warning: Please remember, all command spaces matter! Make sure you placed the actual fully qualified path to your executable instead of the corresponding placeholder.

The first command installs the service MyService into your Windows system as a service-own process. The second one removes the previously installed MyService service.

What’s Next?

The next topic, to be presented in Part II, will cover a case of a newer service style adopted since Windows 2000. You’ll meet the new (extended) version of the service control map and take a more intense look at the service initialization phase.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read