Click here to Skip to main content
Click here to Skip to main content

Simple service base class for Windows

By , 23 Mar 2004
 

Introduction

The purpose of this code is to show how to make a simple C++ class which wraps WinNT API for service programs. With this class, you can implement a Windows service in a few minutes. This class is not made for advanced service programming, but it should not be too hard modify this source to append any additional functionality. When I implemented this class, I was trying to copy the interface of ServiceBase class from .NET framework. I think it's good design for most requirements of service applications.

Have on mind that using this class you can't implement multiple services in one application. I will implement this in future updates and add more functionality.

Using the code

I didn't provide a sample project this time but here is the code you need for that:

#include <windows.h>
#include <stdio.h>
#include "smart_reference.h"
#include "I_ServiceBase.h"

void WriteLog(const char* message)
{
    FILE* pFile = fopen("C:\\test_service.log","a");
    if(pFile)
    {
        fwrite(message, sizeof(char), strlen(message), pFile); 
        fclose(pFile);
    }
}

class TestService : public CI_ServiceBase
{
public:
    TestService()
    {
        m_bStop = false;
    }

    ~TestService()
    {
        CI_ServiceBase::~CI_ServiceBase();
    }

    virtual void OnStart(int argc, LPTSTR* argv)
    {
        WriteLog("Server started.\n\r");
        while(!m_bStop)
        {
            Sleep(1000);
        }
    }
    virtual void OnStop()
    {
        WriteLog("Server stopped.\n\r");
        m_bStop = true;
    }

    virtual void OnPause()
    {
        WriteLog("Server paused.\n\r");
    }

    virtual void OnContinue()
    {
        WriteLog("Server continued.\n\r");
    }

private:
    bool m_bStop;
};

TestService ts;

int main(int argc, char* argv[])
{
    ts.set_CanPauseAndContinue(false);
    ts.set_ServiceName("isw_test");
    CI_ServiceBase::Run((TestService*)ts);
    return 0;
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

EasyWay
Web Developer
Bosnia And Herzegovina Bosnia And Herzegovina
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalbizarre hard-coded service name buffer lengthmemberdamnedyankee7 Nov '06 - 3:39 
What is the point of this ?
 
char m_strName[182];
 

I mean, how did you come up with 182 as a buffer size ?
 
Was it just a random number, or did you discover some hidden limit to the size of a service name ?

Generalincorrect use of SERVICE_WIN32 in CreateService / SetServiceStatusmemberdamnedyankee7 Nov '06 - 2:50 
I am confused by your use of SERVICE_WIN32 as the default ServiceType.
 
The help for SERVICE_STATUS states that the dwServiceType should be one of the values listed, yet SERVCIE_WIN32 is a bitwise or of two of the possible values - and seems to be use primarily for enumeration of services.
 
Members
dwServiceType
Type of service. This member can be one of the following values. Value Meaning
SERVICE_FILE_SYSTEM_DRIVER The service is a file system driver.
SERVICE_KERNEL_DRIVER The service is a device driver.
SERVICE_WIN32_OWN_PROCESS The service runs in its own process.
SERVICE_WIN32_SHARE_PROCESS The service shares a process with other services.
 

 



GeneralBit manipulationmemberhobyrne24 Oct '05 - 11:39 
File: cservice_base_src.zip : I_ServiceBase.cpp
 
void CI_ServiceBase::set_CanHandlePowerEvent(bool b)
{
//SET_BIT(this->m_CanHandle, P_CAN_HANDLE_POWER_EVENT, b);
if(b)
{
m_Status.dwControlsAccepted |= SERVICE_ACCEPT_POWEREVENT;
}
else
{
m_Status.dwControlsAccepted ^= SERVICE_ACCEPT_POWEREVENT;
}
}
 
Please tell me you're kidding. Do you know what ^ means? It means XOR, it means toggle. set_CanHandlePowerEvent(true) will always turn on the flag, but set_CanHandlePowerEvent(false) will not always turn it off.

GeneralRe: Bit manipulationmemberEasyWay30 May '06 - 2:46 
Ok, I was not long time here. But here is the answer just for the archive.
 
if SERVICE_ACCEPT_POWEREVENT bits are set already, this operation:
m_Status.dwControlsAccepted ^= SERVICE_ACCEPT_POWEREVENT; will do turn off, that's for shure.
 
Thank You Smile | :)
 
Faruk Kasumovic.
Student of Electrical Enginering (Information Technologies) in Tuzla, Bosnia and Herzegovina.
GeneralRe: Bit manipulationmemberMike C#13 Oct '06 - 6:22 
Just so I understand, is this what you want to achieve in this code?
 
set_CanHandlePowerEvent(true) = turn "on" SERVICE_ACCEPT_POWEREVENT
 
set_CanHandlePowerEvent(false) *and* SERVICE_ACCEPT_POWEREVENT is currently "on" = turn "off" SERVICE_ACCEPT_POWEREVENT
set_CanHandlePowerEvent(false) *and* SERVICE_ACCEPT_POWEREVENT is currently "off" = turn "on" SERVICE_ACCEPT_POWEREVENT
 
This is what your code currently does.   Just making sure I understand.   Thanks.
GeneralRe: Bit manipulationmemberdamnedyankee7 Nov '06 - 2:55 
Clearly what is really desired is this:
 
void CI_ServiceBase::set_CanHandlePowerEvent(bool b)
{
if(b)
{
m_Status.dwControlsAccepted |= SERVICE_ACCEPT_POWEREVENT;
}
else
{
m_Status.dwControlsAccepted &= ~SERVICE_ACCEPT_POWEREVENT;
}
}
 
which has the expected results of:
 
set_CanHandlePowerEvent(true) = turn "on" SERVICE_ACCEPT_POWEREVENT
set_CanHandlePowerEvent(false) = turn "off" SERVICE_ACCEPT_POWEREVENT

Generalinstall/uninstall -- unmanaged c++memberasmotritsky3 Jun '04 - 15:36 
how do i install/uninstall a service once i've written it?
GeneralNot bad... missing a few pieces...memberPeter Mares24 Mar '04 - 23:13 
Cute article. However, your class seems to be missing some pieces...
 
1. When a service starts up, a standard initialisation function is called which, as the name implies, initialises the service and any resources that may be needed. What if a programmer using your class wanted to do more in the initialisation?
 
2. How does one send hints to the SCUM during a lengthy initialisation process?
 
Cheers,
Peter
 

www.kinkycode.com
[Glossary Manager] [AfterThought Backup Lite]

99 little bugs in the code, 99 little bugs,
Fix 1 bug, recompile....
101 little bugs in the code...
GeneralRe: Not bad... missing a few pieces...memberEasyWay31 Mar '04 - 3:47 
Thanks, I will add it in next article update.
 
Faruk Kasumovic.
Student of Electrical Enginering (Information Technologies) in Tuzla, Bosnia and Herzegovina.
GeneralFiles not found.....memberJosema24 Mar '04 - 3:44 
Eek! | :eek:
GeneralQuestionmemberdog_spawn24 Mar '04 - 2:28 
~TestService()
{
  CI_ServiceBase::~CI_ServiceBase(); 
}
Strange! What is this all about?
GeneralRe: QuestionmemberJohn M. Drescher24 Mar '04 - 6:24 
Yes, Calling the destructor twice has got to cause problems...
 
John
GeneralRe: QuestionsussAnonymously24 Mar '04 - 6:30 
dog_spawn wrote:
Strange! What is this all about?
 
The drunken cat is calling the base class destructor, to make sure things get cleaned up. Strange indeed. Poke tongue | ;-P
GeneralRe: QuestionmemberEasyWay31 Mar '04 - 3:45 
In case when base class destructor is not declared as virtual, he will not be called in derived class destructor, you have to call it explicit to release any resources used by base class. It's C++.
 
When I wrote the sample code I just do it for habit, but it should not produce the problem even if base class destructor is virtual.

 
Faruk Kasumovic.
Student of Electrical Enginering (Information Technologies) in Tuzla, Bosnia and Herzegovina.
GeneralRe: Questionmemberdamnedyankee6 Nov '06 - 23:09 
>>In case when base class destructor is not declared as virtual, he will not be called in derived class destructor, you have to call it explicit to release any resources used by base class. It's C++.
 

NO IT'S NOT C++ !!!
 
#include
 
using std::cout;
using std::endl;
 
class b_virt
{
public:
b_virt(){}
virtual ~b_virt(){ cout << "b_virt virtual destructor" << endl; }
};
 
class b_std
{
public:
b_std(){}
~b_std(){ cout << "b_std destructor" << endl; }
};
 
class d_virt : public b_virt
{
public:
d_virt(){}
~d_virt(){ cout << "d_virt derived destructor" << endl; }
};
 
class d_std : public b_std
{
public:
d_std(){}
~d_std(){ cout << "d_std derived destructor" << endl; }
};
 
int main(int argc, char* argv[])
{
d_virt dv;
d_std ds;
 
return 0;
}
 

produces this:
 
d_std derived destructor
b_std destructor
d_virt derived destructor
b_virt virtual destructor
 
Notice there is no explicit call to either destructor, yet *somehow*, this code magically calls the virtual destructor in class b_virt. That is C++ !
 
It is not necessary to call the base clas destructor from within the derived class, the compiler will generate code to do this, just as it generates code to call destructors on class members.
 

>>When I wrote the sample code I just do it for habit, but it should not produce the problem even if base class destructor is virtual.
 
It is very dangerous to assume it is safe to call a destructor twice. Your habit is dangerous, and you should stop it. Your code is otherwise quite good.
GeneralRe: QuestionmemberGyuri29 Jul '07 - 2:40 
Maybe it worth to clarify this virtual destructor thing a bit.
The problem occurs, when you have derived classes which allocates system resources in overridden virtual methods. If your destructor is not virtual, it can happen that these resources will not be released at all. A small - stupid - example:
 

class base {
public:
~base() { /* nothing to do */ }
virtual const char * getString() { return "a string"; }
}
 
class derived : public base {
public:
derived() : m_str(0) {}
 
~derived() {
if ( m_str != 0 ) {
free(m_str);
}
}
 
const char * getString() {
if ( m_str == 0 ) {
m_str = strdup("another string");
}
return m_str;
}
 
private:
char * m_str;
}
 
...
 
base * a = new base();
derived * b = new derived();
base * c = new derived();
 
// virtual method works well:
cout << a.getString(); // call of base::getString() - 'a string'
cout << b.getString(); // call of derived::getString() - 'another string'
cout << c.getString(); // call of derived::getString() - 'another string'
 
// as the destructor is NOT virtual, it will NOT work similar:
delete a; // call of ~base()
delete b; // call of ~derived()
delete c; // oops! call of ~base() - derived::m_str is not cleaned up!!!

 
Solution of such problems is clear: use virtual destructor.
As a rule of thumb, use virtual destructor if you has virtual method.
I hope this helps to clarify virtual destructor a bit.
 

 
Gy

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 24 Mar 2004
Article Copyright 2004 by EasyWay
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid