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
   상태로 먼저 만들고, 필요한 작업을 한 후 마지막에 해당 상태로 변환 되었음을 알려주어야 합니다.


지금까지는 전체 소스 샘플없이, 부분 코드만으로 작업을 진행했습니다만, 다음 진행되는 부분 부터는
프로젝트를 만들어서 소스를 업데이트 시키고, 파일로 첨부하겠습니다.

+ Recent posts