지난 내용
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]

[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]

이번 장에서는 실제로 서비스 프로그램이 어떻게 구성되어 있는지 간단한 샘플을 이용하여 살펴보고자 한다.
샘플에 구현된 기능은 다음과 같다.
1. 설치/제거
2. 시작/중지/일시중지/재시작
3. 설치시 각종 파라미터 설정
4. 디버깅을 위한 간단한 조작
5. 전체 서비스 프로그램의 흐름
6. 기능은 지난 서비스 설명시 잠깐 나온 땡땡거리는 서비스를 기반으로 한다.

소스의 구성 품목은 다음과 같다.
1. main.cpp 서비스 구조를 가진 소스
2. ServiceUtil.h / cpp 서비스를 개발할 때 간혹 쓸모있는 함수 모음 클래스
3. ServiceMgr.h / cpp  서비스의 설치나 동작을 제어하는 기능을 가진 모음 클래스

아래는 접어놓은 부분은 main.cpp 와 두개 클래스의 헤더만 설명을 위하여 올려 놓았다.
전체 샘플은 압축하여 첨부 파일로 추가할 것이다.




서비스 본체를 구성하고 있는 main.cpp 를 살펴보기로 한다.
int main(int argc, char** argv)
{
#if 0
    /////////////////////////////////////////////////////////////////////
    // #define 로 쌓인 이 구문은 첨부된 클래스 2개의 간단한 사용법과
    // 정상적인 지를 확인하기 위한 테스트 코드이다. 불필요하면 스킵해도 된다.
    
/////////////////////////////////////////////////////////////////////

    CServiceUtility u;
    u.ServiceUserBlankPassword(FALSE);

    u.UserPrivileges("Administrtor", L"SeServiceLogonRight");
    u.ProcessPrivileges(GetCurrentProcess(), SE_SHUTDOWN_NAME, TRUE);

    PWTS_PROCESS_INFO sinfo = NULL;
    DWORD count = 0;
    u.WTSEnumProcesses(sinfo, &count);
    u.WTSFree(sinfo);

    PWTS_SESSION_INFO info = NULL;
    count = 0;
    u.WTSEnumSessions(info, &count);
    u.WTSFree(info);

    CServiceUtility::CWTSSession ws(WTS_CURRENT_SERVER_HANDLE, 0);

    char buffer[512] = {0};
    if(u.GetOSDisplayString(buffer))
        printf("%s\n", buffer);

    CServiceUtility su;
    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi = {0};
    u.CreateProcessToDesktop("C:\\Windows\\System32\\cmd.exe", NULL, si, pi, 0);
    WaitForSingleObject(pi.hProcess, INFINITE);

#endif

    // 서비스를 관리하는 매니저와 구현을 도와주는 유틸 클래스
    
CServiceManager manager;
    CServiceUtility utility;

    // 아래 항목은 설정하지 않으면, 파일정보를 읽어서 자동으로채운다.
    manager.ConfigServiceName("CROWBACK");
    manager.ConfigServiceDisp("CROWBACK SERVICE");
    manager.ConfigServiceDesc("CROWBACK's test service, forever ting... ting...");

    // 먼저 서비스로 구동중인지, 응용 어플리케이션 모드로 구동 중인지를 확인한다.
    if(utility.IsServiceMode() == FALSE)
    {
        // 응용 어플리케이션 모드로 돌아갈 때는 인자를 하나 받는다. 인자는 아래와 같다.
        // 인자가 없을 경우는 직접 인자를 입력 받는다. 지정된 값이 아니면 구동 상태로 돌입한다.
        char ch = 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");

            printf("input command: ");
            ch = getch();
        }
        else
            ch = argv[1][0];

        // 인자에 따라 아래와 같은 동작을 수행한다.
    
    switch(ch)
        {
        case 'i':
            manager.Install();
            return 0;
        case 'u':
            manager.Uninstall();
            return 0;
        case 's':
            manager.Start();
            return 0;
        case 't':
            manager.Stop();
            return 0;
        case 'p':
            manager.Pause();
            return 0;
        case 'c':
            manager.Continue();
            return 0;

        // 위의 값이 아니면 그냥 응용 어플리케이션 모드로 구동한다.

        default:
            return service_main(argc, argv);
        }
    }

   // 여기로 왔다는 것은 현재 서비스 모드로 구동중인 상태인 것이다.
   SERVICE_TABLE_ENTRY STE[] =
   {
       //SCM (Service Control Manager)에 등록하기 위하여 지정된 구조체 정보를 채운다.
       { manager.m_InstallParam.lpServiceName, (LPSERVICE_MAIN_FUNCTION)service_main },
       { NULL, NULL}
   };
      
   // 해당 채워진 구조체를 이용하여 서비스 메인 함수를 SCM이 호출할 수 있도록 등록작업을 마친다.
   if(StartServiceCtrlDispatcher(STE) == FALSE)
       return -1;

   return 0;
}


여기서는 콘솔 응용프로그램의 main 함수에서 어떻게 서비스 메인함수를 등록하는지 간단한 절차를 처리해보았다.
하나의 페이지에 꽤 긴 내용이 들어가서 편집작업에 어려움이 있어 추가 설명과 나머지 내용을 다음장에서 정리할 것이다.

+ Recent posts