Programming/윈도우 서비스

[서비스] 뼈다귀 서비스 코드 [7/?]

까막백 2009. 10. 15. 10:28

지난 내용
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]
[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 [6/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 Cont. [6/?]

이전 장에서 다룬 서비스 본체 코딩이라고 한 부분은 유틸성 클래스를 이용하여, 본체를 구성하여 보았다.
다음 주제로 넘어가기 이전에, 잡다한거 다 제거하고 순수하게.. API 만으로 작성한 코드를 살펴보면서
차이점이 무엇인지? 구지 저런거 다 넣고 진행할 필요가 있는지. 급할 때는 기본 코드만으로 쓱~~ ^^;;;

아래의 코드는 이전 샘플과 거의 동일한 기능을 제공한다. 그리고, 실제로도 대부분 저렇게 구성하는 경우가 많다.

#include <windows.h>
#include <winsvc.h>

#define SRVNAME "CROWBACK Service"
SERVICE_STATUS_HANDLE hss = NULL;
SERVICE_STATUS ss = {SERVICE_WIN32_OWN_PROCESS, 0, 0xFF, 0, 0, 0, 0};

DWORD GetServiceRunStatus(SERVICE_STATUS& ss);
DWORD GetServiceRunStatus();
DWORD WINAPI service_handler(DWORD fdwCtrl, DWORD dwEvent, LPVOID lpData, LPVOID lParam);
int service_main(int argc, char** argv);

// 1. 원래의 메인 함수
int main(int argc, char** argv)
{
    SERVICE_TABLE_ENTRY STE[] =
   {
       {SRVNAME, (LPSERVICE_MAIN_FUNCTION)service_main},
       {NULL,NULL}
   };

   if(StartServiceCtrlDispatcher(STE) == FALSE)
       return -1;

    return 0;
}

// 2. 서비스의 메인 함수
int service_main(int argc, char** argv)
{
    hss = RegisterServiceCtrlHandlerEx(SRVNAME, service_handler, NULL);
    if (hss == NULL)
        return -1;

    ss.dwCurrentState = SERVICE_START_PENDING;
    SetServiceStatus(hss, &ss);

    // bla bla...

    ss.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hss, &ss);

    while(GetServiceRunStatus() != SERVICE_STOPPED)
    {
       if(GetServiceRunStatus() == SERVICE_PAUSED)
       {
           Sleep(1000);
           continue;
       }

        ::MessageBeep(0xFFFFFFFF);
       Sleep(1000);
    }

    return 0;
}

// 3. 이벤트 핸들러 함수.
DWORD
WINAPI service_handler(DWORD fdwCtrl, DWORD dwEvent, LPVOID lpData, LPVOID lParam)
{
    switch (fdwCtrl)
   {
   case SERVICE_CONTROL_PAUSE:
        ss.dwCurrentState = SERVICE_PAUSE_PENDING;
        SetServiceStatus(hss, &ss);
        // to do
        ss.dwCurrentState = SERVICE_PAUSED;
        SetServiceStatus(hss, &ss);
        break;

   case SERVICE_CONTROL_CONTINUE:
        ss.dwCurrentState = SERVICE_CONTINUE_PENDING;
        SetServiceStatus(hss, &ss);
        // to do
        ss.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus(hss, &ss);
        break;

   case SERVICE_CONTROL_STOP:
        ss.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(hss, &ss);
        // to do
        ss.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus(hss, &ss);
        break;

   default:
        break;
   }

    return NO_ERROR;
}

// 4. 서비스의 상태를 읽어오는 함수.
DWORD GetServiceRunStatus(SERVICE_STATUS& ss)
{
    SC_HANDLE hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if(hScm == NULL)
        return ERROR_INVALID_HANDLE;

    SC_HANDLE hSrv = OpenService(hScm, SRVNAME, SERVICE_INTERROGATE);
    if (hSrv == NULL)
        return GetLastError();

    if(ControlService(hSrv, SERVICE_CONTROL_INTERROGATE, &ss) == 0)
    {
        DWORD dw = GetLastError();
        if(dw == ERROR_SERVICE_NOT_ACTIVE)
        {
            ss.dwCurrentState = SERVICE_STOPPED;
            return ERROR_SUCCESS;
        }

        return dw;
    }

       CloseServiceHandle(hSrv);
    CloseServiceHandle(hScm);

    return ERROR_SUCCESS;
}

DWORD GetServiceRunStatus()
{
    SERVICE_STATUS ss = {0};
    if(GetServiceRunStatus(ss) != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    return ss.dwCurrentState;
}

위의 소스를 빌드하면, 바로 서비스 프로그램이 생성된다. 단, 저건 스스로 설치할 수 없다, 스스로 제거할 수 도 없다.
외부에서 해당 서비스의 이름을 가지고, 이전 장에서 주욱 나열해왔던 설치부터 실행 제거까지 전부 외부에 의존해야 한다.

이전에 설명했던거 다 필요없이 저거면 서비스 프로그램이 되는 것이다. 급할 때 헤딩하지 말고, Ctrl+C, Ctrl+V 신공을 발휘하여 
빠르게 진행할 때 사용하기 위해서, 만들어둔 템플리트 처럼 사용하고 있는 코드이기도 하다.