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

The Part I and Part II articles gave the samples of using SFL in practice. Now, it's time to delve a bit into the topic and find out what is under the SFL's hood.

Two chains of class inheritance are present in SFL. The service class chain is:

CServiceProxyT<> -> CYourService -> CServiceBaseT<> ->
   CServiceRoot

The application class chain looks sufficiently more modest:

CYourApp -> CServiceAppT<>

See details regarding the mentioned classes below in corresponding sections.

As mentioned in Part I, the SFL expects that the console application type is used for building the service application. This is defined by the SFL_BEGIN_SERVICE_MAP macro that implements the _tmain function. The service application class instance is constructed statically before calling the main function—please remember this, it is implementing a customized application class.

Function _tmain

After _tmain gets called, the service class object is constructed. Any information about it is passed to the application object (to the CServiceAppT<>::Main member function) that finally calls StartServiceCtrlDispatcher internally.

The _tmain() function constructs the service map object (in factm it's an array of CServiceRoot* pointers) whose entries are initialized by values returned by one of the CServiceProxyT<>::Construct static member function versions.

Each map entry initialization call looks like a single SFL_SERVICE_ENTRY(2) macro within the SFL_BEGIN_SERVICE_MAP macro and SFL_END_SERVICE_MAP macro braces. The latter macro finishes the map with a NULL item.

Note: Developers familiar with Unicode programming already have recognized the well-known _tmain macro that eventually gets resolved to main or wmain, depending on the _UNICODE project definition.

Class CServiceRoot

All supplementary service functions are implemented by the CServiceRoot class. They provide information about the service (GetServiceName, GetCurrentStatus, or GetControlsAccepted) and set its status appropriately (SetServiceStatus, SetServiceStatusSpecific). The CheckPoint function serves to properly report a pending status.

Class CServiceBaseT<>

The CServiceBaseT<> class defines the service part of the framework structure and implements one vital part of service code: the ServiceMain member function.

The only additional task performed by the CServiceBaseT<> class is the registration of the proper _Handler(Ex) function according to a service style. The style of the service is defined by the serviceStyle enumeration declared within the SFL_BEGIN_CONTROL_MAP(_EX) macro. SFL_BEGIN_CONTROL_MAP defines the SFL_SERVICESTYLE_NT4 value and the SFL_BEGIN_CONTROL_MAP_EX macro defines SFL_SERVICESTYLE_NT5 appropriately.

Also, the CServiceBaseT<> class contains two void member functions, InitInstance and GetServiceContext. The first one is called inside ServiceMain, and its return value will be affected if the service instance will report to SCM a status SERVICE_RUNNING (in case of successful initialization) or SERVICE_STOPPED (in case of initialization failure).

The GetServiceContext function is called only in case of SFL_SERVICESTYLE_NT5 style, providing a context to a HandlerEx function. You may consider a context like some structure or interface that supplies all data required for proper service functioning. Although the service class itself may be used as service context structure, GetServiceContext provides an additional level of flexibility available to the developer.

Please, refer to the MSDN HandlerEx article regarding service context additional details.

Note: Both these member functions can be overridden in your service class, if this is required.

Your Service Class

CYourService is your own class that implements the essence of your service—control code handling.

Your service class implements a control map and handler methods for corresponding control codes. In fact, a pair of SFL_BEGIN_CONTROL_MAP(_EX) and SFL_END_CONTROL_MAP(_EX) macros construct a non-static member function, Handler(Ex), where each SFL_HANDLE_CONTROL_XXX entry binds the certain control code to its handler.

Of course, all handlers must be implemented in your class. Depending on the service style, they must conform to the appropriate handler prototype.

// SFL_BEGIN_CONTROL_MAP()
typedef DWORD (T::*t_handler)(DWORD&, DWORD&, BOOL&);
typedef DWORD (T::*t_handler_range)(DWORD, DWORD&, DWORD&, BOOL&);

// SFL_BEGIN_CONTROL_MAP_EX()
typedef DWORD (T::*t_handler_ex)(DWORD&, DWORD&, DWORD&, BOOL&,
   DWORD, LPVOID, LPVOID);
typedef DWORD (T::*t_handler_range_ex)(DWORD, DWORD&, DWORD&,
   DWORD&, BOOL&, DWORD, LPVOID, LPVOID);

Each prototype corresponds to the appropriate SFL_HANDLE_CONTROL_XXX macro:

SFL_HANDLE_CONTROL(code, handler)
SFL_HANDLE_CONTROL_RANGE(codeMin, codeMax, handler)
SFL_HANDLE_CONTROL_EX(code, handler)
SFL_HANDLE_CONTROL_RANGE_EX(codeMin, codeMax, handler)

It's important to remember that non-extended control handler macros/prototypes are usable within the SFL_BEGIN_CONTROL_MAP_EX control map as well as their extended versions (see the demo project provided in Part II).

As mentioned above, your class may implement the GetServiceContext and InitInstance functions as well. Remember, an InitInstance call must return true to let your service start running.

Note: Please, do not use any initialization and resource allocation inside the class constructor because you will have an opportunity do this inside the InitInstance call (see the next section). In case your application runs in console user mode, your service class even cannot be used.
Note: Some of you might think a complementary ExitInstance may exist in the framework to let you de-initialize/de-allocate your service class data structures. Sorry, guys, you are wrong; any service de-initialization must be done in the SERVICE_CONTROL_STOP handler.

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

Class CServiceProxyT<>

The CServiceProxyT<> class is mainly intended for service instantiation. It creates your service class object and integrates your code into the application part of the framework. It also interfaces with SCM, providing the translation layer for callbacks from SCM to your service code.

The SFL_SERVICE_ENTRY macro parameter provides a class name for the CServiceProxyT<> template instantiation along with a unique service id.

Note: The purpose of the unique id is to distinguish among service clones. The clones will be explained in Part IV.

The purpose of the proxy class is to provide static member functions _ServiceMain and _Handler(Ex) that will become known to SCM as ServiceMain and Handler(Ex) functions of the corresponding service. The proxy class object redirects a call to the appropriate service class non-static member function.

Class CServiceAppT<>

The CServiceAppT<> class defines the application part of the framework structure and implements a vital part of the application code, the Main member function that constructs and initializes the SERVICE_TABLE_ENTRY array that finally gets passed to the Run member function where the StartServiceCtrlDispatcher Win32 API entry is called. Here is the point where your application turns to a service (at last!); this call results in invoking SCM functionality, when the static CServiceProxy<>::_ServiceMain member function gets called in a new thread esspecially created by SCM, while the main thread remains suspended until your service stops. As you may suppose, this call eventually ends in calling the ServiceMain member function of your service class.

Your Application Class

It's time to get back to the _tmain function. At the very beginning, you can see that the pointer to your application class is gotten as a return value of the SflGetServiceApp function. The latter constructs your application object statically and returns its constant pointer.

Note:This function also may be called anywhere and anytime you need to access the application class object—in your service class, for example.

And, when the service map object is initialized, the application object begins to work. First, the PreMain member function is called. Its purpose is to process the application initialization (parse and analyze the command line, read environment variables, initialize or allocate some resources, and so forth) and finally get a decision about whether the application must run in service mode. If PreMain returns TRUE, the service must run in service mode; therefore, the Main function is called next. But, in case the application runs in console user mode, PreMain must not return until application shutdown, when it finally returns FALSE. The PostMain function is called before the application ends in any mode, letting it release all resources allocated in PreMain.

Your application class may implement PreMain and PostMain in case it requires some application initialization/de-initialization routines. Otherwise, the base class function (with void function body) will be called.

Note: In case your application does not require any specific functionality, the CSimpleServiceApp class, defined by SFL, is at your disposal. The class merely instantiates the CServiceAppT<> template class—and does nothing more than that.

Two ways of service entry declaration (SFL_SERVICE_ENTRY(2) macros mentioned above in Function _tmain section) are provided by SFL:

SFL_SERVICE_ENTRY( TService, idRes )
SFL_SERVICE_ENTRY2( TService, id, name )

In the first case, your service class name and resource id are used to construct the service object. The resource id specifies the string table entry to be used as the service name and additionally provides a unique service id required for correct service class instantiation.

In the second case, the unique service id and service name string are provided explicitly and independently to each other.

What's Next?

The next and the last article in the SFL series, Part IV, will cover the additional abilities of service implementation. It will show how to implement a multi-service application, and a service clone creation will be presented there, as well as a description for a supplementary class intended to control the services.

Download: SFL20_base_src_.zip



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • 10 Rules that Make or Break Enterprise App Development Projects In today's app-driven world, application development is a top priority. Even so, 68% of enterprise application delivery projects fail. Designing and building applications that pay for themselves and adapt to future needs is incredibly difficult. Executing one successful project is lucky, but making it a repeatable process and strategic advantage? That's where the money is. With help from our most experienced project leads and software engineers, …

  • As more and more organizations migrate to the cloud, many are faced with hidden costs emerging from unexpected places. Two non-obvious and non-trivial factors can drive up costs. First are separate charges for everything from server memory to intrusion detection. Second are the high personnel costs for early-generation, manually operated clouds. These costs can rack up quickly, creating total cost of ownership (TCO) surprises. Keeping TCO low in the cloud is essentially a matter of management strategy. IT …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds