이번 장에서는 실제로 서비스 프로그램이 어떻게 구성되어 있는지 간단한 샘플을 이용하여 살펴보고자 한다. 샘플에 구현된 기능은 다음과 같다.
1. 설치/제거
2. 시작/중지/일시중지/재시작
3. 설치시 각종 파라미터 설정
4. 디버깅을 위한 간단한 조작
5. 전체 서비스 프로그램의 흐름
6. 기능은 지난 서비스 설명시 잠깐 나온 땡땡거리는 서비스를 기반으로 한다.
소스의 구성 품목은 다음과 같다. 1. main.cpp 서비스 구조를 가진 소스
2. ServiceUtil.h / cpp 서비스를 개발할 때 간혹 쓸모있는 함수 모음 클래스
3. ServiceMgr.h / cpp 서비스의 설치나 동작을 제어하는 기능을 가진 모음 클래스
아래는 접어놓은 부분은 main.cpp 와 두개 클래스의 헤더만 설명을 위하여 올려 놓았다.
전체 샘플은 압축하여 첨부 파일로 추가할 것이다.
switch(fdwControl) { caseSERVICE_CONTROL_PAUSE: manager.SetServiceRunStatus(SERVICE_PAUSE_PENDING); // 서비스를 일시 중지 시킨다. manager.SetServiceRunStatus(SERVICE_PAUSED); break;
caseSERVICE_CONTROL_CONTINUE: manager.SetServiceRunStatus(SERVICE_CONTINUE_PENDING); // 일시 중지 시킨 서비스를 재개한다. manager.SetServiceRunStatus(SERVICE_RUNNING); break;
caseSERVICE_CONTROL_STOP: manager.SetServiceRunStatus(SERVICE_STOP_PENDING); // 서비스를 멈춘다 (즉, 종료와 같은 의미) // 서비스를 종료하면, service_main 는 절대로 리턴하지 않는다. // 그러므로 해제하려면 작업이 있으면 모든것을 이곳에서 처리한다. manager.SetServiceRunStatus(SERVICE_STOPPED); break;
/* 설정하지 않으면, 기본 파일값으로 파일 이름을 잘라다가 자동으로 설정한다. */ manager.ConfigServiceName("CROWBACK"); manager.ConfigServiceDisp("CROWBACK SERVICE"); manager.ConfigServiceDesc("CROWBACK's test service, forever ting... ting...");
if(utility.IsServiceMode()==FALSE) { charch=0; if(argc!=2) { printf("sample.exe [option: i, u, s, t, p, c]\n"); printf(" i - install\n"); printf(" u - uninstall\n"); printf(" s - start\n"); printf(" t - stop\n"); printf(" p - pause\n"); printf(" c - continue\n\n");
CHARlpServiceStartName[MAX_PATH]; /* if lpServiceStartName is Not NULL LSA_HANDLE Policy = GetPolicyHandle(); BYTE buffer[1024] = {0}; PSID sid = (PSID)buffer; if(GetUserSID(sid, "user name")) UserPrivileges(sid, Policy, L"SeServiceLogonRight"); */
CHARlpPassword[MAX_PATH]; /* if lpServiceStartName is Not NULL and lpPassword is NULL HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Lsa -> limitblankpassworduse (DWORD) 0 Disable - Blank Password use 1 Enable - No. */
SERVICE_FAILURE_ACTIONSsfa; SC_ACTIONsfaAction[3]; /* if service failed, what do you want? ChangeServiceConfig2 (SERVICE_CONFIG_FAILURE_ACTIONS) SC_ACTION::Type SC_ACTION_NONE, -- default, SC_ACTION_RESTART, SC_ACTION_REBOOT, SC_ACTION_RUN_COMMAND SC_ACTION::Delay if Type != SC_ACTION_NONE default delay 2000 ms
remark) index begin 0 - 1 - 2 */ CHARsfaReBootMessage[MAX_PATH]; CHARsfaRetryCommand[MAX_PATH]; UINTsfaFailureCounter;
/*---------------------------- | Service Control Functions | ----------------------------*/
// install service with configured param DWORDInstall();
// uninstall service, if force is true, and timeover -> kill process DWORDUninstall(BOOLbForce=FALSE,DWORDdwWait=30/* sec */);
// start service with timeout DWORDStart(DWORDdwWait=30/* sec */);
// stop service with timeout DWORDStop(BOOLbForce=FALSE,DWORDdwWait=30/* sec */);
// pause service with timeout DWORDPause(DWORDdwWait=30/* sec */);
// continue service with timeout DWORDContinue(DWORDdwWait=30/* sec */);
// retrive service run status DWORDGetServiceRunStatus(SERVICE_STATUS&ss); DWORDGetServiceRunStatus();
// set service run status DWORDSetServiceRunStatus(DWORDdwStatus);
// set service event handle store VOIDSetServiceHandler(SERVICE_STATUS_HANDLEssh); SERVICE_STATUS_HANDLEGetServiceHandler();
/*----------------------------------------- | Service Install Param Config Functions | -----------------------------------------*/
// set service unique name ( must be, default fill) VOIDConfigServiceName(LPCTSTRlpszStr);
// set service display name ( must be, default fill) VOIDConfigServiceDisp(LPCTSTRlpszStr);
// set service description ( optional ) VOIDConfigServiceDesc(LPCTSTRlpszStr);
// set service execute file full path ( must be, default fill) VOIDConfigServiceExec(LPCTSTRlpszStr);
// specfic startup username and password ( optional ) VOIDConfigStartUser(LPCTSTRlpszUser,LPCTSTRlpszPassword);
// dependency other services ( optional ) VOIDConfigDependencies(LPCTSTRlpszStr);
// if service start failed, next step? ( optional, default fill) VOIDConfigSfaAction(UINTuIDX,SC_ACTION_TYPEaction,UINTdelay); VOIDConfigSfaRebootMessage(LPCTSTRlpszStr); VOIDConfigSfaRetryCommand(LPCTSTRlpszStr); VOIDConfigSfaCounterReset(UINTcount);
public: staticSRVARGm_InstallParam;
protected: VOIDConfigInitialize(); };
서비스 본체를 구성하고 있는 main.cpp 를 살펴보기로 한다.
intmain(intargc,char**argv) { #if0 ///////////////////////////////////////////////////////////////////// // #define 로 쌓인 이 구문은 첨부된 클래스 2개의 간단한 사용법과 // 정상적인 지를 확인하기 위한 테스트 코드이다. 불필요하면 스킵해도 된다. /////////////////////////////////////////////////////////////////////
// 서비스를 관리하는 매니저와 구현을 도와주는 유틸 클래스 CServiceManagermanager; CServiceUtilityutility;
// 아래 항목은 설정하지 않으면, 파일정보를 읽어서 자동으로채운다. manager.ConfigServiceName("CROWBACK"); manager.ConfigServiceDisp("CROWBACK SERVICE"); manager.ConfigServiceDesc("CROWBACK's test service, forever ting... ting...");
// 먼저 서비스로 구동중인지, 응용 어플리케이션 모드로 구동 중인지를 확인한다. if(utility.IsServiceMode()==FALSE) { // 응용 어플리케이션 모드로 돌아갈 때는 인자를 하나 받는다. 인자는 아래와 같다. // 인자가 없을 경우는 직접 인자를 입력 받는다. 지정된 값이 아니면 구동 상태로 돌입한다. charch=0; if(argc!=2) { printf("sample.exe [option: i, u, s, t, p, c]\n"); printf(" i - install\n"); printf(" u - uninstall\n"); printf(" s - start\n"); printf(" t - stop\n"); printf(" p - pause\n"); printf(" c - continue\n\n");
// 인자에 따라 아래와 같은 동작을 수행한다. switch(ch) { case'i': manager.Install(); return0; case'u': manager.Uninstall(); return0; case's': manager.Start(); return0; case't': manager.Stop(); return0; case'p': manager.Pause(); return0; case'c': manager.Continue(); return0;
// 위의 값이 아니면 그냥 응용 어플리케이션 모드로 구동한다. default: returnservice_main(argc,argv); } }
// 여기로 왔다는 것은 현재 서비스 모드로 구동중인 상태인 것이다. SERVICE_TABLE_ENTRYSTE[]=
{ //SCM (Service Control Manager)에 등록하기 위하여 지정된 구조체 정보를 채운다.
{ manager.m_InstallParam.lpServiceName,(LPSERVICE_MAIN_FUNCTION)service_main }, { NULL, NULL} }; // 해당 채워진 구조체를 이용하여 서비스 메인 함수를 SCM이 호출할 수 있도록 등록작업을 마친다. if(StartServiceCtrlDispatcher(STE)==FALSE) return-1;
return0;
}
여기서는 콘솔 응용프로그램의 main 함수에서 어떻게 서비스 메인함수를 등록하는지 간단한 절차를 처리해보았다.
하나의 페이지에 꽤 긴 내용이 들어가서 편집작업에 어려움이 있어 추가 설명과 나머지 내용을 다음장에서 정리할 것이다.