메뉴 닫기

Simple Windows Service in C++

Introduction

This article shows how to create a basic Windows Service in C++. Services are very useful in many development scenarios depending on the architecture of the application.

Background

There are not many Windows Service examples that I found in C++. I used MSDN to write this very basic Windows service.

Using the code

At a minimum a service requires the following items:

  • A Main Entry point (like any application)
  • A Service Entry point
  • A Service Control Handler

You can use a Visual Studio template project to help you get started. I just created an “Empty” Win32 Console Application.
Before we get started on the Main Entry Point, we need to declare some globals that will be used throughout the service. To be more object oriented you can always create a class that represents your service and use class members instead of globals. To keep it simple I will use globals.
We will need a SERVICE_STATUS structure that will be used to report the status of the service to the Windows Service Control Manager (SCM).
Hide Copy Code

SERVICE_STATUS        g_ServiceStatus = {0};

We will also need a SERVICE_STATUS_HANDLE that is used to reference our service instance once it is registered with the SCM.
Hide Copy Code

SERVICE_STATUS_HANDLE g_StatusHandle = NULL;

Here are some additional globals and function declarations that will be used and explained as we go along.
Hide Copy Code

SERVICE_STATUS        g_ServiceStatus = {0};
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;
VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler (DWORD);
DWORD WINAPI ServiceWorkerThread (LPVOID lpParam);
#define SERVICE_NAME  _T("My Sample Service")
Main Entry Point

Hide Copy Code

int _tmain (int argc, TCHAR *argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
        {NULL, NULL}
    };
    if (StartServiceCtrlDispatcher (ServiceTable) == FALSE)
    {
        return GetLastError ();
    }
    return 0;
}

In the main entry point you quickly call StartServiceCtrlDispatcher so the SCM can call your Service Entry point (ServiceMain in the example above). You want to defer any initialization until your Service Entry point, which is defined next.

Service Entry Point

Hide Shrink Copy Code

VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv)
{
    DWORD Status = E_FAIL;
    // Register our service control handler with the SCM
    g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler);
    if (g_StatusHandle == NULL)
    {
        goto EXIT;
    }
    // Tell the service controller we are starting
    ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus));
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
    if (SetServiceStatus (g_StatusHandle , &g_ServiceStatus) == FALSE)
    {
        OutputDebugString(_T(
          "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
    /*
     * Perform tasks necessary to start the service here
     */
    // Create a service stop event to wait on later
    g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL)
    {
        // Error creating event
        // Tell service controller we are stopped and exit
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
	{
	    OutputDebugString(_T(
	      "My Sample Service: ServiceMain: SetServiceStatus returned error"));
	}
        goto EXIT;
    }
    // Tell the service controller we are started
    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugString(_T(
          "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
    // Start a thread that will perform the main task of the service
    HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
    // Wait until our worker thread exits signaling that the service needs to stop
    WaitForSingleObject (hThread, INFINITE);
    /*
     * Perform any cleanup tasks
     */
    CloseHandle (g_ServiceStopEvent);
    // Tell the service controller we are stopped
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;
    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugString(_T(
          "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
EXIT:
    return;
}

The Service Main Entry Point performs the following tasks:

  • Initialize any necessary items which we deferred from the Main Entry Point.
  • Register the service control handler which will handle Service Stop, Pause, Continue, Shutdown, etc control commands. These are registered via the dwControlsAccepted field of the SERVICE_STATUSstructure as a bit mask.
  • Set Service Status to SERVICE_PENDING then to SERVICE_RUNNING. Set status to SERVICE_STOPPED on any errors and on exit. Always set SERVICE_STATUS.dwControlsAccepted to 0 when setting status to SERVICE_STOPPED or SERVICE_PENDING.
  • Perform start up tasks. Like creating threads/events/mutex/IPCs/etc.
Service Control Handler

Hide Shrink Copy Code

VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
{
    switch (CtrlCode)
	{
     case SERVICE_CONTROL_STOP :
        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
           break;
        /*
         * Perform tasks necessary to stop the service here
         */
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 4;
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugString(_T(
              "My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error"));
        }
        // This will signal the worker thread to start shutting down
        SetEvent (g_ServiceStopEvent);
        break;
     default:
         break;
    }
}

The Service Control Handler was registered in your Service Main Entry point. Each service must have a handler to handle control requests from the SCM. The control handler must return within 30 seconds or the SCM will return an error stating that the service is not responding. This is because the handler will be called in the context of the SCM and will hold the SCM until it returns from the handler.
I have only implemented and supported the SERVICE_CONTROL_STOP request. You can handle other requests such as SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_INTERROGATE, SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_SHUTDOWN and others supported by the Handler or HandlerEx function that can be registered with the RegisterServiceCtrlHandler(Ex) function.

Service Worker Thread

Hide Copy Code

DWORD WINAPI ServiceWorkerThread (LPVOID lpParam)
{
    //  Periodically check if the service has been requested to stop
    while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
    {
        /*
         * Perform main service function here
         */
        //  Simulate some work by sleeping
        Sleep(3000);
    }
    return ERROR_SUCCESS;
}

This sample Service Worker Thread does nothing but sleep and check to see if the service has received a control to stop. Once a stop control has been received the Service Control Handler sets the g_ServiceStopEventevent. The Service Worker Thread breaks and exits. This signals the Service Main routine to return and effectively stop the service.

Installing the Service

You can install the service from the command prompt by running the following command:
Hide Copy Code

C:\>sc create "My Sample Service" binPath= C:\SampleService.exe

A space is required between binPath= and the value[?]. Also, use the full absolute path to the service executable.
You should now see the service in the Windows Services console. From here you can start and stop the service.

Uninstalling the Service

You can uninstall the service from the command prompt by running the following command:
Hide Copy Code

C:\>sc delete "My Sample Service"

		

51 Comments

  1. Dominque Braxton

    I simply want to tell you that I’m newbie to blogs and seriously savored your web site. Likely I’m planning to bookmark your blog . You definitely come with really good posts. Many thanks for revealing your blog site.

  2. go to this web-site

    I uncovered your blog website on google and also inspect a few of your very early posts. Continue to maintain the very good operate. I just added up your RSS feed to my MSN News Viewers. Seeking onward to learning more from you in the future!?

  3. Darnell Peard

    An outstanding share, I simply provided this onto an associate that was doing a little evaluation on this. And he actually bought me breakfast because I discovered it for him. smile. So let me rephrase that: Thnx for the reward! Yet yeah Thnkx for spending the time to review this, I really feel strongly about it and also like learning more on this subject. Preferably, as you become know-how, would certainly you mind updating your blog site with more information? It is extremely valuable for me. Large thumb up for this blog post!

  4. Vella Massar

    I was very delighted to locate this web-site. I wanted to thanks for your time for this terrific read!! I most definitely enjoying every little bit of it as well as I have you bookmarked to take a look at new stuff you article.

  5. recommended you read

    This is the ideal blog for anybody that wishes to discover this subject. You recognize a lot its virtually hard to suggest with you (not that I actually would want?HaHa). You most definitely placed a brand-new spin on a subject thats been covered for several years. Terrific things, simply wonderful!

  6. try this web-site

    There are absolutely a lot of details like that to consider. That is an excellent indicate bring up. I provide the ideas over as basic ideas yet plainly there are inquiries like the one you bring up where the most essential thing will certainly be working in truthful good faith. I don?t recognize if best methods have emerged around points like that, but I make sure that your job is plainly recognized as a fair game. Both children and also ladies feel the influence of just a moment?s enjoyment, for the remainder of their lives.

  7. site web

    Area on with this article, I genuinely believe this internet site requires a lot more consideration. I?ll most likely be once again to check out much more, many thanks for that details.

  8. 사설카지노

    Hello there! I could have sworn I’ve visited this blog before but after browsing through some of the articles I realized it’s new to me. Anyhow, I’m definitely delighted I came across it and I’ll be book-marking it and checking back often!

  9. 바카라사이트

    This is the best blog site for any individual that wants to find out about this subject. You recognize so much its virtually tough to suggest with you (not that I in fact would want?HaHa). You most definitely put a new spin on a topic thats been blogged about for years. Excellent stuff, simply wonderful!

  10. 카지노사이트

    Greetings from California! I’m bored to tears at work so I decided to check out your site on my iphone during lunch break. I love the information you present here and can’t wait to take a look when I get home. I’m surprised at how quick your blog loaded on my cell phone .. I’m not even using WIFI, just 3G .. Anyways, good site!|

댓글 남기기

이메일은 공개되지 않습니다.