본문 바로가기

[ Windows Program ]/Windows API

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

2.    ServiceMain 서비스 제어 처리자(ServiceCtrlHandler)

 

윈도우즈 NT 서비스프로그램은 WinMain() 이외에 2개의 다른 함수를 윈도우에 export 하여야 한다. 그중 하나가 ServiceMain() 함수로서 이름은 고정된 것이 아니다. 지난 강좌에서 보았던 WinMain() 함수의 제일 마지막 부분에 서비스 제어자로서 등록하는 부분이 있는 이때 ServiceMain() 함수의 이름을 서비스 제어 관리자(SCM-Service Control Manager) 등록한다.

 

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

 

  if ( argc < 2 ) {

    if ( !StartServiceCtrlDispatcher(DispatchTable) )

    {

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

    }

  }

 

서비스 제어 관리자(SCM) 의해 시작된 서비스 프로그램은 ServiceMain() 함수를 실행하게 되는 , 이때 서비스 메인에서 해야할 일은 크게 2가지 이다.

 

  1. 서비스 제어 처리자(Service Control Handler) 등록한다.
  2. while 루프를 사용하여 서비스 프로그램의 실행을 유지한다.

 

서비스 제어 처리자는 서비스 제어 관리자에 의해 서비스 실행에 대한 변화가 생겼을 처리를 하는 루틴이다. 예를 들어 누군가가 서비스의 실행을 중지 시킨다면 서비스 관리자는SERVICE_CONTROL_STOP 코드를 서비스 제어 처리자에 보내 서비스 프로그램으로 하여금 서비스를 종료하게 한다.

물론 이러한 요청은 지난 강좌에서 필자의 LXSVC 클래스의 SCM 제어 함수에 의한 프로그래밍 방법으로도 가능하며 또는 사용자가 제어판->관리자툴->서비스 프로그램을 통해서도 가능하다.

 

서비스 제어 처리자를 등록하는 것은 아래와 같이 간단한 함수 호출로 이루어진다.

 

SERVICE_STATUS_HANDLE ServiceStatusHandle = RegisterServiceCtrlHandler( ECHOSVC_NAME, ServiceCtrlHandler );

 

if ( ServiceStatusHandle == (SERVICE_STATUS_HANDLE) 0 )

{

  DbgOut("EchoServer : RegisterServiceCtrlHandler() failed %d\n", GetLastError() );

  return;

}

 

서비스 메인 함수의 두번째 일인 while 루프를 사용하여 서비스 프로그램의 실행을 유지하는 것은 반드시 필요한 것은 아니다. 예를 들어 서비스가 시스템 시작시에만 잠깐 실행되어도 된다면 굳이 실행을 유지할 이유는 없다. 다만 본래 서비스의 정의 자체가 운영체제의 일부로서 운영체제를 지원하는 것임을 생각할 , 서비스의 실행이 시스템 종료까지 유지되는 것이 바람직하다는 것이 일반적인 서비스 프로그램의 모습일 것이다.

 

필자의 예에선g_isRunning 라는 boolean 변수를 두어 변수가 참인 동안 while 루프를 반복하게 하였다. 변수는 서비스 제어 처리자에SERVICE_CONTROL_STOP 코드가 넘어오면 거짓이 되어 ServiceMain() 함수를 종료하게 된다.

 

bool g_isRunning = false;

 

  g_isRunning = true;

 

  while (g_isRunning)

  {

    DbgOut("EchoServer : Service running...\n");\

 

    Sleep(1000);      

  }

 

 

서비스 제어 처리자(Service Control Handler) 앞서 밝혔듯이 서비스 제어 관리자(SCM) 보내는 제어 코드를 처리하는 함수이며 ServiceMain() 함수에 의해 등록이 된다. 서비스 제어 처리자가 받는 가장 일반적인 제어 코드는SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_STOP이며 필자의 예제 함수에 보듯이 처리 상황은 상당히 단순하다.

 

VOID WINAPI ServiceCtrlHandler(DWORD dwControl)

{

  DbgOut("EchoServer : ServiceCtrlHandler\n");

 

  switch(dwControl)

  {

    case SERVICE_CONTROL_PAUSE:

      DbgOut(" - SERVICE_CONTROL_PAUSE\n");

      g_lxSvc->SetServiceStatus(SERVICE_PAUSED);

      break;

 

    case SERVICE_CONTROL_CONTINUE:

      DbgOut(" - SERVICE_CONTROL_CONTINUE\n");

      g_lxSvc->SetServiceStatus(SERVICE_RUNNING);

      break;

 

    case SERVICE_CONTROL_STOP:

      DbgOut(" - SERVICE_CONTROL_STOP\n");

      g_isRunning = false;

      g_lxSvc->SetServiceStatus(SERVICE_STOPPED);

      break;

 

    case SERVICE_CONTROL_INTERROGATE:

      DbgOut(" - SERVICE_CONTROL_INTERROGATE\n");

      break;

 

    default:

      DbgOut("EchoServer : unrecognized control code %ld\n", dwControl );

  }

  return;

}

 

그러나 서비스 상태를 지정해 주는 것은 어렵진 않지만 서비스 상태 값의 못된 지정에 의해 실제 프로그래밍 시에 상당한 곤란을 겪을 수도 있다. 여기서 자세히 다룰 필요는 없다고 생각되며 자세한 것은 LXSVC 클래스의SetServiceStatus() 멤버함수를 참조하기 바란다.

 

이것으로서 대략적인 서비스 프로그램의 윤곽은 살펴보았다고 있다. 윈도우즈 프로그램이 기본적으로 따르는 모양(WinMain() WndProc() 함수로 구성되는 모양) 있듯이 서비스 프로그램 역시 기본적인 모양이 있다. 글을 읽는 독자들은 서비스 프로그램을 작성해야 일이 있을 기본 프로그램 틀로서 필자의 예제를 가져다 놓고 시작하면 좋을 것이다.

 

다음 강좌에서는 실제로 윈속을 다루는 클래스를 보이고자 한다.

 

- Reference

  http://cafe.naver.com/nevernding