const char* S_NAME = "TEST";
const char* S_DISP = "TEST Service";
const char* S_DESC = "TEST Service Description";

VOID _tmain_service(INT ARGC, LPSTR* ARGV);
DWORD WINAPI _tmain_service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);

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) 을 받을 때, 그것을 받을 수 있도록 콜백형식의
    // 함수를 등록하는 것이다.

    SERVICE_STATUS_HANDLE hd = RegisterServiceCtrlHandlerEx(S_NAME, _tmain_service_handler, NULL);
    if (hd == 0)
        return;

    // 요기가 콘솔의 main() 함수 역활을 하는 곳이다.
    // 먼가 기능을 넣으려면 이곳에다가 만들면 된다.  
  

    return;
}

DWORD WINAPI _tmain_service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
    // 서비스라 좋은점이 있네, 시스템에서 발생하는 잡다구리한 이벤트들을 별로 힘들이지 않고
    // 이곳에서 받아다가 처리할 수 있다.


    return 0;
}

살펴보면, 간단한 Windows Console Application 에다가 기본 main() 함수를 잠깐 개조한것 정도로 밖에는 보이지 않는다.
이렇게 처리하면서 서비스용 메인함수서비스 제어용 제어함수 2개를 등록하면 기본 서비스 구조는 끝이다.

서비스의 장점은 일단 윈도우가 로그온/로그오프 상태에서도 정상적으로 동작한다는 것이고,
단점은, 일반적인 어플리케이션에서 사용하는 많은 기능이 제약된다는 것이다. 디버깅도 좀 취약하다.
           특히나, 서버계열에서는 간단한 EnumWindows 같은 함수도 원하는데로 동작하지 않는다. -_-;;;;
           즉, 특정 터미널 세션이나, 데스크톱 윈도우의 제어 기능(일반 어플들의 기능)같은 것들을 처리할라믄 꽤 고달프다는 말이다.
           메인 함수야 제대로 블록해놓고 구성해도 되지만 제어기능의 내부는 블록되면 다른 서비스에도 영향을 미친다.

자, 이렇게 기본 서비스의 구조를 살펴봤으니, 다음장에서는 간단하게 띵띵~ 소리를 내는 서비스를 만들어 보자.
서비스의 시작(START), 중지(STOP), 일시 정지(PAUSE), 이어가기(CONTINUE) 동작도 처리해보자.

+ Recent posts