본문 바로가기

[ Windows Program ]/Windows API

멀티쓰레드 윈도우즈 소켓 프로그래밍 #2 – 윈도우즈 서비스 프로그래밍

1.    윈도우즈 서비스 프로그래밍

 

윈도우즈 NT 플랫폼은 서비스라는 프로그램 형태를 지원한다. 이러한 서비스는 시스템 서비스라는 이름으로 불리며 윈도우즈 시스템이 시작하여 종료할 때까지 배후에서 실행되면서 유저 또는 시스템에 특정 서비스를 제공하는 프로그램이다. 이러한 시스템 서비스 프로그램은 운영체제의 일부로서 동작하기 때문에 작성시 주의가 필요하다.

 

장에서는 윈도우즈 시스템 서비스 프로그래밍에 대해 알아보고 간단한 서비스 프로그램을 작성해 보고자 한다.

 

시스템 서비스 프로그램은 일반적으로 다음과 같은 3개의 export 함수를 가지게 된다.

첫번째 함수는 main() 함수로서 일반적인 콘솔 어플리케이션과 동일하다. , 서비스 프로그램은 콘솔 어플리케이션 이기도 하다. Main() WinMain() 함수로도 대체 있다.

 
#include "Winsvc.h"
main()
{
  SERVICE_TABLE_ENTRY Table[]={{"SampleService1",ServiceMain},
                               {NULL,NULL}};
  StartServiceCtrlDispatcher(Table);
}

 

위의 예는 가장 간단한 뿐이다. 위의 예에서 main() 함수의 입무는 서비스 콘트롤 디스패치 핸들러를 실행시키는 뿐이다.

그러나 일반적인 서비스 프로그램은 자체적인 서비스 인스톨/언인스톨 루틴을 포함하고 있으며 명령라인에서 옵션을 통해 프로그램의 서비스를 제어한다.

 

예제에선 4개의 옵션을 지원하며 각각의 옵션은 다음과 같다.

 

-i : 서비스를 인스톨한다.

-d : 서비스를 종료하고 제거한다.

-start : 종료되어 있는 서비스를 시동시킨다.

-stop : 실행중인 서비스를 종료시킨다.

 

이러한 예가 들어간 main() 함수는 아래와 같으며 서비스를 핸들링 하기 위해 본인이 작성한 LXSVC 클래스를 호출하고 있다.

 

#include <stdio.h>

#include <wtypes.h>

#include <winnt.h>

#include <winsvc.h>

#include <winuser.h>

#include "DbgOut.h"

#include "LxSvc.h"

 

#define ECHOSVC_NAME    "Test Echo Service"

#define ECHOSVC_DESC    "Win32 API Cafe example"

 

VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);

VOID WINAPI ServiceCtrlHandler(DWORD dwControl);

 

bool g_isRunning = false;

 

LXSVC *g_lxSvc = NULL;

 

int main( int argc, char* argv[] )

{

  SERVICE_TABLE_ENTRY DispatchTable[] = { { ECHOSVC_NAME, ServiceMain }, { 0, 0 } };

 

  g_lxSvc = new LXSVC;

  if ( argc > 1 && !( stricmp(argv[1], "-i") ) )

       {

    if ( g_lxSvc->InstallService(NULL, ECHOSVC_NAME, ECHOSVC_DESC) ) {

      g_lxSvc->StartService(NULL, ECHOSVC_NAME);

    }

  }

  if ( argc > 1 && !( stricmp(argv[1], "-d" ) ) ) // Uninstall the Service

  {

    g_lxSvc->UnistallService(NULL, ECHOSVC_NAME);

  }

  if ( argc > 1 && !( stricmp(argv[1], "-stop" ) ) ) // Stop the Service

  {

    g_lxSvc->ControlService(NULL, ECHOSVC_NAME, SERVICE_CONTROL_STOP);

  }

  if ( argc > 1 && !( stricmp(argv[1], "-start" ) ) ) // Start the Service

  {

    g_lxSvc->StartService(NULL, ECHOSVC_NAME);

  }

  if ( argc < 2 ) {

    if ( !StartServiceCtrlDispatcher(DispatchTable) )

    {

        DbgOut("EchoServer : StartServiceCtrlDispatcher error = %d\n", GetLastError() );

    }

  }

  delete g_lxSvc;

  g_lxSvc = NULL;

  return 0;

}

 

LXSVC 클래스의 멤버함수 이름은 지극히 직관적이기 때문에 이해하기 어렵지 않을 것이다. 서비스를 추가, 제거, 시작, 종료 등의 제어를 위해선 서비스 제어 관리자(Service Control Manager = SCM) 다루어야 한다. 자세한 내용은 LXSVC 클래스 내부의 함수를 보도록 하며 강좌에선 서비스를 등록하는 부분만 살펴 봄으로서 SCM 프로그래밍을 살짝만 맛보기로한다.

 

bool LXSVC::InstallService(LPTSTR tszRemoteMachine, LPTSTR tszServiceName, LPTSTR tszDisplayName, LPTSTR tszModulePathName)

{

  DbgOut("LXSVC::InstallService\n");

  bool bRet = false;

  if ( m_bWin9x ) {

 

  } else {

    TCHAR tszBuffer[255];

    TCHAR tszModulePath[MAX_PATH];

 

    SC_HANDLE hSvcMgr = OpenSCManager(tszRemoteMachine, NULL, SC_MANAGER_ALL_ACCESS);

 

    if ( hSvcMgr ) {

      if ( tszModulePathName != NULL ) {

        _tcscpy(tszModulePath, tszModulePathName);

      } else {

        GetModuleFileName(GetModuleHandle(NULL), tszModulePath, MAX_PATH);

      }

      _tcscpy( tszBuffer, "\"" );

      _tcscat( tszBuffer, tszModulePath );

      _tcscat( tszBuffer, "\"" );

 

                SC_HANDLE hSvc = CreateService(hSvcMgr, tszServiceName, tszDisplayName, SERVICE_ALL_ACCESS,

                                     SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,

                                     tszBuffer, NULL, NULL, NULL, NULL, NULL );

      if ( hSvc ) {

        bRet = true;

        CloseServiceHandle(hSvc);

      } else {

        DbgOut("LXSVC::InstallService - CreateService error = %d\n", GetLastError());

      }

      CloseServiceHandle(hSvcMgr);

    } else {

      DbgOut("LXSVC::InstallService - OpenSCManager error = %d\n", GetLastError() );

    }

  }

  return bRet;

}

 

위의 예에서InstallService() 멤버함수는 먼저 서비스 제어 관리자 SCM 열고(OpenSCManager()) 핸들을 사용하여 새로운 서비스를 생성(CreateService())하고 있다. 모든 서비스 제어 함수는 위와같이 SCM 열고 SCM안의 서비스 핸들을 생성 또는 열은 핸들을 사용하여 제어하게 된다. 물론 모든 사용이 끝난 후는 서비스 핸들을 닫는다.

 

다음 강좌에서는 서비스의 다른 2 중요한 함수인 서비스의 메인 함수 (ServiceMain()) 서비스 제어자(ServiceCtrlHandler()) 대해 알아보기로 한다.



- Reference

  http://cafe.naver.com/nevernding