[윈도우 서비스 프로그램] 기본 구조에 살을 붙혀보자.
const char* S_NAME = "TEST";
const char* S_DISP = "TEST Service";
const char* S_DESC = "TEST Service Description";
// 서비스를 제어하기 위한 핸들을 저장하고 있어야한다.
SERVICE_STATUS_HANDLE srvhd = 0;
VOID _tmain_service(INT ARGC, LPSTR* ARGV);
DWORD WINAPI _tmain_service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);
VOID SERVICE_STATE(DWORD dwState, DWORD dwAccept);
int main(int argc, char** argv)
{
SERVICE_TABLE_ENTRY STE[] =
{
{(char*)S_NAME, (LPSERVICE_MAIN_FUNCTION)_tmain_service},
{NULL,NULL}
};
// 서비스를 위해서 특별히 만들어진 구조의 함수 시작 부분을 시스템에 전달해야 한다.
// 콘솔 프로그램과 다른 부분은 이렇게 등록시킨 함수가 콘솔의 main 처럼 동작한다는 점이다.
// 일종의 콜백함수 포인터를 등록하면, 서비스 매니저가 이걸 호출해주는 방식이다.
if(StartServiceCtrlDispatcher(STE) == FALSE)
return -1;
return 0;
}
VOID _tmain_service(INT ARGC, LPSTR* ARGV)
{
// 서비스가 외부 제어 명령(시작, 중지, 다시 시작... etc) 을 받을 때, 그것을 받을 수 있도록 콜백형식의
// 함수를 등록하는 것이다.
srvhd = RegisterServiceCtrlHandlerEx(S_NAME, _tmain_service_handler, NULL);
if (srvhd == NULL)
return;
// 무한 루프를 돌면서 띵띵~ 소리를 낸다.
while(1)
{
::MessageBeep(0xFFFFFFFF);
Sleep(1000);
}
return;
}
// 서비스의 상태를 변경 시켜주는 함수.
VOID SERVICE_STATE(DWORD dwState, DWORD dwAccept)
{
SERVICE_STATUS ss;
ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState=dwState;
ss.dwControlsAccepted=dwAccept;
ss.dwWin32ExitCode=0;
ss.dwServiceSpecificExitCode=0;
ss.dwCheckPoint=0;
ss.dwWaitHint=0;
SetServiceStatus(srvhd, &ss);
}
DWORD WINAPI _tmain_service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
// 서비스라 좋은점이 있네, 시스템에서 발생하는 잡다구리한 이벤트들을 별로 힘들이지 않고
// 이곳에서 받아다가 처리할 수 있다.
switch (fdwControl)
{
case SERVICE_CONTROL_PAUSE:
SERVICE_STATE(SERVICE_PAUSE_PENDING,0);
// 서비스를 일시 중지 시킨다.
SERVICE_STATE(SERVICE_PAUSED);
break;
case SERVICE_CONTROL_CONTINUE:
SERVICE_STATE(SERVICE_CONTINUE_PENDING,0);
// 일시 중지 시킨 서비스를 재개한다.
SERVICE_STATE(SERVICE_RUNNING);
break;
case SERVICE_CONTROL_STOP:
SERVICE_STATE(SERVICE_STOP_PENDING, 0);
// 서비스를 멈춘다 (즉, 종료와 같은 의미)
SERVICE_STATE(SERVICE_STOPPED);
break;
default:
break;
}
return NO_ERROR;
}
간단하게 샘플을 완성해 보려고 했더니, 좀더 알고 있어야 하는 사항들이 자꾸 생기네요..
_tmain_service_handler 에 추가된 내용
각각의 서비스 상태 변화에 맞추어 SERVICE_STATE 라는 함수를 호출합니다.
위 함수의 기능은 현재 이 서비스는 이러한 상태입니다.... 라고 서비스 관리자 (SCM)에
전달해 주는 기능을 합니다. 이것을 빼놓고 동작만 구현하면 서비스 관리자에서 해당
서비스의 상태를 정상적으로 보여 줄 수 없기 때문에 잘못된 동작을 일으킬 수 있습니다.
서비스는 상태를 변화 시키기 이전에 "상태 변화를 준비중입니다". 라고 하는 PENDDING
상태로 먼저 만들고, 필요한 작업을 한 후 마지막에 해당 상태로 변환 되었음을 알려주어야 합니다.
지금까지는 전체 소스 샘플없이, 부분 코드만으로 작업을 진행했습니다만, 다음 진행되는 부분 부터는
프로젝트를 만들어서 소스를 업데이트 시키고, 파일로 첨부하겠습니다.