Framework for Writing Services and Multithreaded Applications, Part 1

I recently started a home project developing a set of middleware tools I am hoping to flog on the Internet. Part of this involved writing NT services and lightweight server side multithreaded applications. While doing this, I got a little sidetracked (as I often do when time permits) and decided to knock up a generic framework for things I find myself (re)writing frequently. This includes:

  1. Lightweight classes add stack tracing to debug and release builds. This can be enabled/disabled at will by using an external utility that uses named kernel objects to send signals.
  2. Performance testing classes to write code timings to CSV files for analysis with Excel.
  3. Generic logging/tracing framework and string building classes.
  4. Generic classes/template classes for thread pooling. This includes mechanisms for starting/ending threads in a synchronous manner by using a single worker thread. An implementation of a high performance IO Completion port thread pool.
  5. Classes that wrap kernel objects and critical sections to protect access to shared resources. Not that useful in itself but the classes can also write diagnostics to CSV files to show locking orders to help trace deadlock scenarios. This also includes a read/write lock class for when there is a requirement for many read threads but few write threads.
  6. Simple-to-use macros for ANSI to UNICODE conversion.
  7. Classes/template classes and macros for rapid development of NT services.
  8. I may also add a few developer hints and tips I find useful along the way.

Some of this you may have seen before, but its nice to find things in one place and, as they say, plagiarism is the best form of progress (as Bill probably once said to Xerox). I also like to think I have expanded on these ideas sufficiently to justify the existance of these articles.

You’ll probably note that there are much newer developer tools out there than VC++ 6.0 that I have used to develop this. I’m using VC++ 6.0 just because it is my favourite editor, it has a relatively small footprint, and I’m too lazy to convert some VBS macros I use for comments and other useful stuff (written by an extremely talented ex-colleague of mine).

All that will be discussed should be compilable in the latest version of Visual Studio or will require just a minor port.

To get the juices flowing (and to see if anyone is still interested in this kind of stuff), this article will discuss implementing an NT service using my framework with a minimal number of lines of application code. If I see enough interest in this article, I will follow with subsequent articles discussing the other points described above.

I’ve actually posted a version of this service framework in my “Start a Command As Any User” article I posted yonks ago, but I never really demonstrated usage of the source because this was not the main object of the article. I’ve recently updated the code for easy of use and installation.

What’s Involved in Writing an NT Service?

Without going into too much detail, writing an NT service requires that you write a ServiceMain and ServiceHandler to handle control requests from the service control manager. These will be called on different threads and can be tricky to implement or (at the very least) requires reading a lot of boring documentation.

This framework provides an implementation for all this and allows you to write code for one or more services to be hosted in the executable by deriving from the MbServices::MbServiceEntry class and providing an implementation of the MbRun virtual function.

Mart’s hot tip:
You’ll probably notice as you peruse my code that I tend to use namespaces frequently. I’ve fairly recently (over the last few years or so) become a big fan of using namespaces. It’s amazing how often you find yourself writing a class with a name you have used many times before. It’s also nice to group together a set of logically related classes with a well designed set of namespaces.

After providing this implementation, which will be called to perform the service functionality, you use a bunch of macros to define a map of the services your executable will provide to the service control manager via the dispatch table.

Once the map has been provided, it’s just a matter of installing and starting the service(s). I’ve added a helper class: MbServices::MbServiceStartup to do this.

Here’s a trivial ‘Hello World’ example:

/*
 ******************************************************************
 * MbMain.cpp : Implementation file.
 * Console entry point.
 *
 * @version                    V1.0
 *
 * @author                     Martyn C Brown
 *
 * @changeHistory
 *   4th January      2007     (V1.0) Creation (MCB)
 ******************************************************************
 */

/*
 ******************************************************************
 * Include all necessary include files
 ******************************************************************
 */
#include "..\MbCommon\MbService.hpp"

/**
 ******************************************************************
 * <P> Derive from MbServices::MbServiceEntry and implement a
 * MbRun(). ; </P>
 *
 * @version     V1.0
 *
 * @author      Martyn C Brown
 *
 * @changeHistory
 *   4th January      2007     (V1.0) Creation (MCB)
 ******************************************************************
 */
class MbGoodbyeWorld : public MbServices::MbServiceEntry
{
public:
   /*
    ***************************************************************
    * Implement the run function.
    ***************************************************************
    */
    virtual void MbRun()
    {
        ::OutputDebugString(_T("Goodbye cruel world!\r\n"));
        ::Sleep(2000);
    }
};

/*
 ******************************************************************
 * The service map needs defining once in a source file to describe
 * the available services.
 ******************************************************************
 */
MbBEGINSERVICEMAP()
    MbSERVICEENTRY5(MbGoodbyeWorld)
MbENDSERVICEMAP()

void _tmain(int argc, TCHAR * argv[])
{
   /*
    ***************************************************************
    * Use the helper class for service startup.
    * On the command line pass: /i to install, /u to uninstall.
    * Anything else then the exe assumes it is being called by the
    * service control manager and attempts to start the service.
    ***************************************************************
    */
    MbServices::MbServiceStartup service;
    service.MbStart(argc, argv);
}

So, if you ignore all the comments, you have just developed a service in 15 lines of code. I hope you agree that this saves a lot of the leg work in writing all the service code you would normally have to worry about.

Because of how MbServices::MbServiceStartup::MbStart() is implemented, you can install the service by calling it from the command line and passing /i as a switch…

Start the services applet and you should see the new service…

Start the service and DebugView to monitor the debug stream and you’ll see the output from the service…

There are a few shortcomings in this service. The main one is that this is little control over the name of the service (which is just the name of the class derived from MbServices::MbServiceEntry) and there is no helpful description of the service that you can view in the services applet.

Mart’s hot tip:

Personally, I find pre-compiled headers a pain. They just don’t always work and you end up spending an hour to find a bug that isn’t there but is a side affect from incorrectly generated code related to pre-compiled headers being out of line.

I prefer to rebuild a project each time from scratch (I don’t even tend to trust incremental linking too much either). If you are working on a network drive, disabling these options can result in a much longer build time so I always change the location of the generated intermediate files to a local C drive.

I generally base the name of this directory on my project. For example, C:\temp\mcbServiceLib\Debug

You’ll be suprised how much quicker this makes your build.

Let’s have a look at another example service that allows you more control over the service name and description. This is done by using a different MbSERVICEENTRYx macro to describe the service to the service control manager (for details, see the comments in the source). Just for fun, you will also host two services in the exe…

/*
 ******************************************************************
 * MbMain.cpp : Implementation file.
 * Console entry point.
 *
 * @version                       V1.0
 *
 * @author                        Martyn C Brown
 *
 * @changeHistory
 *   30th December    2006        (V1.0) Creation (MCB)
 ******************************************************************
 */

/*
 ******************************************************************
 * Include all necessary include files
 ******************************************************************
 */
#include "..\MbCommon\MbService.hpp"

/**
 ******************************************************************
 * <P> Derive from MbServices::MbServiceEntry and implement a
 * MbRun().  </P>
 *
 * @version     V1.0
 *
 * @author      Martyn C Brown
 *
 * @changeHistory
 *   30th December    2006       (V1.0) Creation (MCB)
 ******************************************************************
 */
class MbFazor : public MbServices::MbServiceEntry
{
public:
   /*
    ***************************************************************
    * Implement the run function.
    ***************************************************************
    */
    virtual void MbRun()
    {
        ::OutputDebugString(_T("Fazors are cool!\r\n"));
        ::Sleep(2000);
    }
};

/**
 ******************************************************************
 * <P> Derive from MbServices::MbServiceEntry and implement a
 * MbRun().  </P>
 *
 * @version     V1.0
 *
 * @author      Martyn C Brown
 *
 * @changeHistory
 *   30th December    2006      (V1.0) Creation (MCB)
 ******************************************************************
 */
class MbGixxer : public MbServices::MbServiceEntry
{
public:
   /*
    ***************************************************************
    * Implement the run function.
    ***************************************************************
    */
    virtual void MbRun()
    {
        ::OutputDebugString(_T("Gixxers are the best!\r\n"));
        ::Sleep(2000);
    }
};
/*
 ******************************************************************
 * The service map needs defining once in a source file to describe
 * the available services.
 ******************************************************************
 */
MbBEGINSERVICEMAP()
    MbSERVICEENTRY2(MbFazor, _T("MartsFazor"), _T("Martyn's Fazor"),
        _T("998cc Silver, slight scratch damage due to careless
            driver"))

    MbSERVICEENTRY2(MbGixxer, _T("MartsGixxer"), _T("Martyn's Gixxer"),
        _T("998cc Blue and white, two bald tyres"))
MbENDSERVICEMAP()

void _tmain(int argc, TCHAR * argv[])
{
   /*
    ***************************************************************
    * Use the helper class for service startup.
    * On the command line pass: /i to install, /u to uninstall.
    * Anything else then the exe assumes it is being called by the
    * service control manager and attempts to start the service.
    ***************************************************************
    */
    MbServices::MbServiceStartup service;
    service.MbStart(argc, argv);
}

Now, if you install the services using /i…

…and refresh the services applet, you’ll see more verbose details…

Mart’s hot tip:

You may disagree, but when I’m writing a re-usable class I like to just be able to include it as a header file and not have to dig around for the .cpp. Adding the .cpp is fine if there are only a couple to add, but when you have many source files it can be a pain.

To work around this, one popular approach is to create a static code library, and then use a pragma in a header file to automatically include the lib…

#pragma comment(lib, "..\\Bin\\MbServiceLibDbgU.lib")

This approach works fine most of the time, and this is how I have developed the service code, but can be painful when you have many variations of your code (for example, UNICODE versions, ANSI versions, DEBUG versions, RELEASE versions, and so on). Then, you end up with a horrible nested precompilation steps with many variations…

#if defined(_DEBUG) || defined(DEBUG)
   #if defined(UNICODE) || defined(_UNICODE)

      #if defined(_DLL)
         #if defined(_MT)
            #pragma comment(lib,
               "..\\Debug\\MbStackTraceLibDbgUDllMT.lib")
         #else
            #pragma comment(lib,
              "..\\Debug\\MbStackTraceLibDbgU.lib")
         #endif //defined(_MT) || defined(_MT)
      #else //defined(_MT)
      #endif //defined(_MT)
   #endif
#else //defined(UNICODE) || defined(_UNICODE)

   #if defined(_DEBUG) || defined(DEBUG)
      #pragma comment(lib, "..\\Debug\\MbStackTraceLib.lib")
   #else //defined(UNICODE) || defined(_UNICODE)
      #pragma comment(lib, "..\\Release\\MbStackTraceLib.lib")
   #endif //defined(UNICODE) || defined(_UNICODE)

#endif //defined(UNICODE) || defined(_UNICODE)

You also have to ensure your library builds all the different configurations.

You also have the worry about versioning—what if you find a bug in your library? Obviously, you need to fix the bug and recompile. Do you then want to recompile everything that uses the library? Otherwise, you will need to maintain different versions of the library so you can recompile the different projects.

Another approach I like to use when possible to resolve some of these issues is to write re-usable classes as template classes. I’ll then create a single specialisation of the template (the actual parameter used not important and unused) so that the code is all contained within the header file. This can also alleviate some of the versioning issues by taking a copy of the header file (or shared code directory) per project.

I’ve done this in this project with MbWinErrorImpl<>. This example is a simple class to convert windows errors codes into string descriptions and manage the associated memory…

template <class nNotUsed> class MbWinErrorImpl
{
public:
   /*
    *****************************************************
    * Return a windows error formatted as a string.
    *****************************************************
    */
    LPTSTR MbGetWinError(DWORD dwErr = 0);
    ...
};

template <class nNotUsed>
LPTSTR MbWinErrorImpl<nNotUsed>::MbGetWinError(DWORD dwErr)
{
    ...
    return m_lpMsg;
}

typedef mcbWinErrorImpl MbWinError;

Caveat: You have to be careful here to implement the function outside of the class itself so that the code is not generated as inline.

Having done this, I then can contain all the source code in one header file and just provide the #include to get the required functionality.

Through the course of these articles, I’ll use this technique quite a lot. You could argue that, in a purist form, this isn’t a correct use of template classes. If you don’t like this approach, feel free to split the files into .cpp/.hpp combinations.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read