H/W 드라이버  
H/W 카다로그  
H/W 메뉴얼  
S/W 개발 메뉴얼  
S/W 기타자료  
S/W 개발 참고자료  
CAD 자료  
   S/W 기타자료
제목 NT 서비스 구현하기 조회수 902
   
등록일자 :  2013-10-21
다운로드 횟수 :  916회


다운로드  제작사링크  국내대리점링크  
 
 
 

마소에 기재되었던 예전 소스입니다.

 

 

NT 서비스 구현하기
--------------------------------------------------------------------------------
NT 서비스는 유닉스의 데몬과 비슷한 개념의 백그라운드(background)로 실행되는
프로그램으로, 사용자가 아닌 NT 시스템에 의해 실행되고 제어되는 프로그램입니다.
NT 서비스는 일반적인 실행파일로 알고리즘 구현은 일반 프로그래밍과 같으나,
내부적으로 서비스 제어 관리자(Service Control Manager, 이하 SCM)에 의해
로드되고, 제어되기 때문에 SCM에서 제공하는 프로그래밍 인터페이스를 따라야
합니다.
NT 시스템에 익숙한 독자분들은 시작|프로그램|관리도구|서비스 대화상자를 실행시켜
현재 시스템에 설치돼 있는 서비스 정보를 구할 수 있고, 제어할 수 있다는 것을
알 수 있을 것입니다. 서비스 대화상자를 통해 서비스는 크게 실행중(RUNNING),
중지(STOP), 일시중지(PAUSE) 상태로 관리되는 것을 볼 수 있는데, 내부적으로는
SCM에 의해 진행중인 상태인 시작중(START_PENDING), 중지중(STOP_PENDING),
일시정지중(PAUSE_PENDING), 재시작중(CONTINUE_PENDING) 상태까지 일곱 가지
상태로 관리합니다.
NT 서비스로서 동작하려면, 즉 SCM에 의해 제어되기 위해선 우선 실행파일이
NT서비스로 설치돼야 합니다. 설치된다는 것은 SCM에서 관리하고 있는
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services 레지스트리 키 하단에
새로운 정보를 등록함을 의미하는데,레지스트리에서 정보를 관리한다고 레지스트리를
직접 조작하는 것은 좋지 않습니다.
NT에서는 SCM제어를 위한 API를 제공하므로 API를 이용하는 것이 안전합니다.
<리스트7>은 Simple Service라는 NT 서비스 예제 프로그램을 시스템에 설치하는
코드입니다.
NT 서비스 설치는 SCM과 연결을 시도하는 OpenSCManager함수와 CreateService함수
호출로 간단히 처리됩니다. 설치된 서비스의 제거는 <리스트8>과 같이 구현됩니다.
SCM과 연결하고, DeleteService함수로 제거하는 흐름은 설치 때와 유사하지만,
OpenService, ControlService등의 함수를 이용해 해당 서비스를 중지시키는 과정이
포함되어 있습니다.
이렇게 설치된 NT 서비스는 SCM에 의해 실행-제어되는데, 이를 위해 NT 서비스
프로그램은 서비스 메인 핸들러와 서비스 제어 핸들러에 해당하는 두개의 훅(hook)함수를
SCM에게 알려줘야 합니다. 서비스 메인 핸들러 등록은 StartServiceCtrlDispatcher함수로,
서비스 제어 핸들러 등록은 RegisterServiceCtrlHandler 함수를 통해 처리됩니다.
<리스트9>는 서비스 메인 핸들러를 등록하는 서비스 시작 처리 함수의 구현 예입니다.

--------------------------------------------------------------------------------
<리스트7> 서비스 설치하기

// SCM에 연결하기

schSCManager = OpenSCManager(
     NULL,     // machine (NULL == local)
     NULL,     // database (NULL == default)
     SC_MANAGER_ALL_ACCESS // access required
    );
    
if (schSCManager)
{
 // 새로운 NT 서비스 프로그램의 정보를 설치한다.
 
 schService = CreateService(
     schSCManager,   // 서비스 이름
     pszSvcName,    // 서비스 출력 이름
     SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
     SERVICE_ERROR_NORMAL, szPath, // 서비스 파일 이름
     NULL, NULL,
     pszDependencies,  // 의존 서비스 목록
     NULL, NULL);
     
     CloseServiceHandle(schSCManager);
}

--------------------------------------------------------------------------------
<리스트8> 서비스 제거하기

// SCM 연결

schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

if (schManager)
{
 schService = OpenService(schSCManager, pszSvcName, SERVICE_ALL_ACCESS);
 if (schService)
 {
  // 서비스 종료 시도
  if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus))
  {
   Sleep(1000);
   while(QueryServiceStatus(schService, &ssStatus))
   {
    if(ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
     Sleep(1000);
    else
     break;
   }
  }
  // 서비스 제거
  if(!DeleteService(schService))
  {
  // 오류 처리
  }
  CloseServiceHandle(schService);
 }
 CloseServiceHandle(schSCManager);
}

--------------------------------------------------------------------------------
<리스트9> 서비스 시작 코드

static SERVICE_TABLE_ENTRY dispatchTable[] =
{
 { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)SimpleServiceMain },
 { NULL, NULL }
};

int SimpleServiceStart(void)
{
 TCHAR szTemp[128];
 
 if (!StartServiceCtrlDispatcher(dispatchTable))
 {
  GetLastErrorText(szTemp, sizeof(szTemp)-1);
  wsprintf(szError, "SIMPLESVC : StartServiceCtrlDispatcher failed = %s", szTemp);
  OutputDebugString(szError);
  return FALSE;
 }
 
 return TRUE;
}

--------------------------------------------------------------------------------

StartServiceCtrlDispatcher함수는 시작할 서비스에 대한 정보를 SERVICE_TABLE_ENTRY
구조체인 dispatchTable을 통해 전달 받습니다. 이 테이블에는 서비스 이름과 서비스
메인 핸들러의 포인터가 들어 있습니다. StartServiceCtrlDispatcher함수는 내부적으로
서비스를 위한 쓰레드를 생성한 후, dispatchTable로 전달된 서비스 메인 핸들러를 호출해
서비스를 시작합니다. <리스트10>은 서비스 메인 핸들러의 예로, 이 핸들러는 시작 부분에
처리해야 할 두가지 중요한 사항이 있습니다.
<리스트10>에서는 우선 RegisterServiceCtrlHandler함수를 호출해 서버 제어 핸들러를
등록하고, SetServiceStatus함수를 이용해 서비스의 상태 변화를 SCM에게 알려줘야 합니다.
메인 핸들러의 상태 변화는 우선 SCM에게 시작중이라는 SERVICE_START_PENDING 상태를
알려준 후, 서비스 시작을 위한 초기화 작업을 수행하고, SERVICE_RUNNING 상태로 변경해
서비스가 시작되었음을 SCM에게 알려줍니다. <리스트11>은 SetServiceStatus 함수를 간편히
호출하기 위해 별도로 작성한 함수입니다.
<리스트11>에서 지정된 서비스 제어 핸들러는 서비스의 상태 변화가 발생하는 경우, 서비스
프로그램이 그에 대응하는 처리를 할 수 있도록 해줍니다. 예제에서는 STOP, PAUSE,
CONTINUE에 대해서만 제어를 받겠다고 했으므로, <리스트12>와 같은 제어 핸들러를 구현해
보았습니다. 제어 핸들러는 전달되는 각 상태마다 PENDING 상태를 둬 해당 처리를 수행하고
ReportStatusToSCMgr을 호출해 요청한 상태로 서비스를 변경합니다.
NT 서비스 예제는 NT서비스의 설치와 삭제 기능을 구현하고 있으며, 서비스 상태가 변할
때마다 디버깅 창으로 그 정보를 출력합니다.
디버깅 창은 '이달의 디스켓'에 포함되어 있는 dbmon.exe를 실행하면 됩니다.

--------------------------------------------------------------------------------
<리스트10> 서비스 메인 핸들러

VOID WINAPI SimpleServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
 // 서비스 제어 핸들러 등록
 sshStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, SimpleServiceCtrl);
 
 if (sshStatusHandle == 0)
  return;
 
 // SCM에게 서비스가 시작중(START PENDING)임을 알려준다.
 if (ReprotStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
 {
  //////////////////////////////////////////////////////////
  // 서비스 초기화 코드
  //////////////////////////////////////////////////////////
  .........
  .........
  
  // SCM에게 서비스가 실행중(RUNNING)임을 알려준다.
  if (ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0)
  {
   BOOL bQuit = FALSE;
   ////////////////////////////////////////////////////////
   // 서비스 메인 코드
   ////////////////////////////////////////////////////////
   ...............
   ...............
  }
 }
 
 if (sshStatusHandle)
  ReportStatusToSCMgr(SERVICE_STOPPED, dwError, 0);
}

--------------------------------------------------------------------------------
<리스트11> 서비스 상태 제어

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
 static DWORD dwCheckPoint = 1;
 BOOL fResult = TRUE;
 
 // 자체 프로세스로 서비스 실행 설정
 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
 
 // 해당 서비스에서 처리할 제어 설정
 ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
 
 // Win32 관리 에러 설정
 ssStatus.dwWin32ExitCode = dwWin32ExitCode;
 
 // 서비스 관련 에러 설정
 ssStatus.dwServiceSpecificExitCode = 0;
 
 // 현재 상태 설정
 ssStatus.dwCurrentState = dwCurrentState;
 
 // SCM의 다른 SetServiceStatus 호출 대기 시간
 ssStatus.dwWaitHint = dwWaitHint;
 
 if((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED))
  ssStatus.dwCheckPoint = 0;
 else
  ssStatus.dwChekPoint = dwCheckPoint++; // 상태 정보가 변경되었음을 SCM에 알려준다.
  
 if (!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus)))
 {
  TCHAR szTemp[256];
  
  GetLastErrorText(szTemp, sizeof(szTemp)-1);
  wsprintf(szError, "SIMPLEVC : SetServiceStatus failed - %s\n", szTemp);
  OutputDebugString(szError);
 }
 
 return fResult;
}

--------------------------------------------------------------------------------
<리스트12> 서비스 제어 핸들러

VOID WINAPI SimpleServiceCtrl(DWORD dwCtrlCode)
{
 switch(dwCtrlCode)
 {
  case SERVICE_CONTROL_STOP :
   // 중지중임을 알려준다.
   ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 3000);
   /////////////////////////////////////////////////////////
   // 서비스 중지를 위한 작업 처리
   ............................
   
   // 중지 상태 설정
   ReprotStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 0);
   break;
   
  case SERVICE_CONTROL_PAUSE :
   // 일시 중지중임을 알려준다.
   ReportStatusToSCMgr(SERVICE_PAUSE_PENDING, NO_ERROR, 3000);
   //////////////////////////////////////////////////////////
   // 서비스 일시중지를 위한 작업 처리
   ...........................
   
   // 일시중지 상태 설정
   ReportStatusToSCMgr(SERVICE_PAUSED, NO_ERROR, 0);
   break;
   
  case SERVICE_CONTROL_CONTINUE :
   // 서비스 재개중임을 알려준다.
   ReportStatusToSCMgr(SERVICE_CONTINUE_PENDING, NO_ERROR, 3000));
   //////////////////////////////////////////////////////////////
   // 서비스 재개를 위한 작업 처리
   .................................
   
   // 실행 상태 설정
   ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0);
   break;
 }
}
 
--------------------------------------------------------------------------------

마소 2001.11

Julian Templenam, "Beginning Windows NT Programming:, WROX Press


 

등록된 댓글이 없습니다.
이름:
비밀번호:
* 자동등록방지입력 [16] 좌측의 자동방지코드를 입력해 주세요. =>