Simple Windows Service in C++

By , 30 May 2013


샘플 다운로드 : SampleService.zip

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).

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.

 SERVICE_STATUS_HANDLE g_StatusHandle = NULL; 

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

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

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.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

Service Entry Point

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_STATUS structure 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

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

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_ServiceStopEvent event. 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:

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

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:

C:\>sc delete "My Sample 







본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
34 리눅스마스터 1급 필기 - 2013/2014년 기출문제 file 졸리운_곰 2014.09.07 562
33 [MFC] Find local network hosts ip, 로컬네트워크의 IP 주소 알아내기 졸리운_곰 2014.08.27 766
32 (Unicode) CString <==> const char* 졸리운_곰 2014.08.27 640
31 SWIG 의 간단한 개요 [C interface to others scripting lanuage] file 졸리운_곰 2014.08.19 472
30 Creating a Child Process with Redirected Input and Output 졸리운_곰 2014.08.17 573
» C++로 짠 간단한 윈도우 서비스 file 졸리운_곰 2014.05.27 1003
28 문자열 빈도수 계산 프로그램 졸리운_곰 2014.03.03 3497
27 문자열 빈도 프로그램 file 졸리운_곰 2014.03.03 1159
26 문자열 검색 함수 - strchr(), strrchr(), strstr(), strspn(), strcspn(), strpbrk() 졸리운_곰 2014.03.03 1258
25 ostream/ifstream C++ stl 문자열 스트림 클래스 졸리운_곰 2014.03.03 1225
24 파일의 존재여부 검사 [access()함수] access() 파일 존재나 접근 권한을 확인합니다. 졸리운_곰 2014.03.03 1290
23 VC++ 파일에서 한 라인씩 읽기 졸리운_곰 2014.03.02 1199
22 linux c 파일 접근권한 정보 가져오기 졸리운_곰 2014.02.28 1163
21 Search Title Of Opened Windows file 졸리운_곰 2014.02.27 978
20 MFC VC++ 파일의 존재유무 체크 졸리운_곰 2014.02.18 1587
19 MFC VC++ : windows tcpip socket Send() function example 졸리운_곰 2014.02.18 1593
18 Check whether one specific process is running on windows with C++ 졸리운_곰 2014.02.18 1099
17 VC++ : Win32 console App run as to be hide (background) 졸리운_곰 2014.02.18 1245
16 VC++ : Win32 console App run as to be hide (background) 졸리운_곰 2014.02.18 1256
15 VC++ : windows : TCPIP SOCKET SERVER and CLIENT 졸리운_곰 2014.02.18 1097
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED