지난 내용
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]
[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 [6/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 Cont. [6/?]
[강좌로 보는 서비스 프로그래밍] 뼈다귀 서비스 코드 [7/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 디버깅 [8/?]
[강좌로 보는 서비스 프로그래밍] 내컴에 누가 왔는지 다알아 [9/?]
[강좌로 보는 서비스 프로그래밍] 환영 인사 [10/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 동작 순서 [11/?]

지난 장에서는 서비스의 동작순서를 개략적으로 살펴보았습니다. 윈도우 2008 서버나, Vista 부터는 좀더 색다른
시작 방식과 안정적인 종료를 지원한다는 것을 공부해 보았는데, 종료 플래그와 관련된 설정이 마무리 되지 않아
정상적으로 종료가 되지를 않더군요. (물론 종료시 처리작업은 마무리가 된듯한...)

서비스의 중지가 아닌, 머신 종료에 따른 서비스가 종료될 때는 다음과 같은 특징이 발견되었네요.
1. SERVICE_CONTROL_SHUTDOWN 와 SERVICE_CONTROL_PRESHUTDOWN 에서 서비스가 정상적으로
   종료되었음을 알리려면 작업이 끝난 뒤에 꼭, SERVICE_STOPPED 상태를 변경 시켜주어야 SCM
   이 정상적으로 
서비스가 종료되었음을 인지하여, 강제로 킬하지 않게 된다.

2. manager.SetPreShutdownInfo(밀리초); 와 같은 형태로 사용할 수 있으며
   위 함수가 작동하려면 SERVICE_CONTROL_PRESHUTDOWN 설정이 미리 되어 있어야 한다.

   그러나, 시간값을 아무리 조절해도 50초를 못넘기고 강제종료된다. 이것을 연장하려면 어떻게 해야할까?..
   MSDN에서는 기본값이 3분이라는데.. 무슨짓을 해도 50초 이상을 못넘긴다. 왜일까?
   (이 문제는 차근 차근 알아보자.. msdn에도 google 에도 더이상의 자료는 찾지 못하였다.)

그리고, 다음으로 이전에는 서비스가 이벤트를 모두 받을 수 있도록 기본값으로 설정하였는데
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_STOP, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_PAUSE_CONTINUE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_SHUTDOWN, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_PARAMCHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_NETBINDCHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_HARDWAREPROFILECHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_POWEREVENT, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_SESSIONCHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_PRESHUTDOWN, TRUE);
위에 코드 처럼 SetServiceEventAcceptor 라고 하는 함수를 추가하여, 원하는 이벤트를 임으로 추가할 수 있도록 별도로 처리하여 놓았다.

만약 SERVICE_ACCEPT_STOP 을 추가하여 놓지 않은 상태에서 서비스를 시작 시키면, 서비스 매니저에 중지 버튼이 비활성화 되어
프로세스를 킬 하지 않는 이상 서비스를 중지 시킬 수 없도록
할 수 도 있다.
같은 이야기로 SERVICE_ACCEPT_PAUSE_CONTINUE 를 빼 놓으면, 일시중지 버튼이 비활성화 된다.

아래는 최종 수정된 서비스의 메인 함수이다.
int service_main(INT ARGC, LPSTR* ARGV)
{
    CServiceManager manager;
    CServiceUtility utility;
    if(utility.IsServiceMode() == TRUE)
    {
        manager.SetServiceHandler(RegisterServiceCtrlHandlerEx(
            CServiceManager::m_InstallParam.lpServiceName, service_handler, NULL));

        if (manager.GetServiceHandler() == NULL)
            return 0;
    }
    else
    {
        printf("run debug mode\npress any key close...\n");
    }


    manager.SetServiceRunStatus(SERVICE_START_PENDING);

    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_STOP, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_PAUSE_CONTINUE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_SHUTDOWN, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_PARAMCHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_NETBINDCHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_HARDWAREPROFILECHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_POWEREVENT, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_SESSIONCHANGE, TRUE);
    manager.SetServiceEventAcceptor(SERVICE_ACCEPT_PRESHUTDOWN, TRUE);

    utility.ProcessPrivileges(GetCurrentProcess(), "SeShutdownPrivilege", TRUE);
    utility.ProcessPrivileges(GetCurrentProcess(), "SeDebugPrivilege", TRUE);

    manager.SetPreShutdownInfo(180000);

    // start ok, i'm ready receive event
    manager.SetServiceRunStatus(SERVICE_RUNNING);


   // 무한 루프를 돌면서 띵띵~ 소리를 낸다.
   while(manager.GetServiceRunStatus() != SERVICE_STOPPED)
   {
       if(manager.GetServiceRunStatus() == SERVICE_PAUSED)
       {
           Sleep(1000);
           continue;
       }

        if(utility.IsServiceMode() == FALSE && _kbhit())
        {
            printf("stop debug mode\n");
            break;
        }

        ::MessageBeep(0xFFFFFFFF);
        printf("ting...\n");
        Sleep(1000);
   }

   return 0;
}

아래는 최종 수정된 서비스의 이벤트 핸들러 이다.
DWORD WINAPI service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
    CServiceManager manager;
    switch (fdwControl)
    {
        // some event code.
  
  case bla bla bla ...:
       break;

        // 윈도우에서 세션에 변경 사항이 발생하였을 경우, 발생하는 이벤트.
    case SERVICE_CONTROL_SHUTDOWN:
        manager.SetServiceRunStatus(SERVICE_STOP_PENDING);
        for(int i=0; i<30; i++)
        {
            Sleep(1000);
            PRTTIME(FileLog);
        }
        manager.SetServiceRunStatus(SERVICE_STOPPED);
        break;

        // 윈도우에서 세션에 변경 사항이 발생하였을 경우, 발생하는 이벤트.
    case SERVICE_CONTROL_PRESHUTDOWN:
        manager.SetServiceRunStatus(SERVICE_STOP_PENDING);
        for(int i=0; i<30; i++)
        {
            Sleep(1000);
            PRTTIME(FileLog);
        }
        manager.SetServiceRunStatus(SERVICE_STOPPED);
        break;

    default
:
       break;
   }

    return NO_ERROR;
}

서비스의 종료 이벤트시 지연시간을 원하는 만큼 충분히 늘여보고자 하였는데... 제대로 마무리가 되지 못하였다.
(기왕 만들어주는거 어디서 어떻게 호출하면, 그리고 어떤 권한이 필요한지 설명을 달아주면 좋을 텐데.. 전무하다 -_-;;;)

나름 대로 열심히 테스트를 진행하고, 자료를 찾아보았지만... 마무리가 않되는 군요..
이거 괜히 건드려서 일만 키운게 아닌지..
(혹시 이쪽 다뤄보신분 있으시면.. 리플좀..  ~~)


아직까지 아무 기능없는 땡땡~ 서비스 이지만 다음에는 먼가 기능을 넣어 보도록 하겠습니다.

+ Recent posts