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

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

이번에는 간단하게 설치된 서비스의 시작, 중지, 일시정지, 일시정지 풀기에 관한 내용을을 짤막하게 다루어 본다.

먼저 서비스 매니저의 핸들을 얻는다. 서비스 매니저의 핸들을 이용하여, 특정 서비스를 제어하는 것이 가능하다.
SC_HANDLE hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

서비스 매니저 핸들을 이용하여, 원하는 서비스를 오픈한다.
SC_HANDLE hSrv = OpenService(hScm, "열고자 하는 서비스의 이름", SERVICE_ALL_ACCESS);

해당 서비스의 핸들을 이용하여, 서비스를 시작한다.
StartService(hSrv, 0, NULL);

해당 서비스의 핸들을 이용하여, 서비스를 중지 시킨다.
SERVICE_STATUS ss;
ControlService(hSrv, SERVICE_CONTROL_STOP, &ss);

해당 서비스의 핸들을 이용하여, 서비스를 일시 중지 시킨다.
SERVICE_STATUS ss;
ControlService(hSrv, SERVICE_CONTROL_PAUSE, &ss);

해당 서비스의 핸들을 이용하여, 일시 중지된 서비스를 재개시킨다.
SERVICE_STATUS ss;
ControlService(hSrv, SERVICE_CONTROL_CONTINUE, &ss);

위와 같은 형태로 서비스의 동작 상태를 변경 하였을 때, 현재의 서비스 동작 상태를 읽어온다.
SERVICE_STATUS ss;
ControlService(hSrv,SERVICE_CONTROL_INTERROGATE,&ss);

// 지금까지 나온 SERVICE_STATUS 의 구조에 대하여 잠깐 살펴보자.
typedef struct _SERVICE_STATUS {
   DWORD   dwServiceType;                     // 서비스의 타입(파일드라이버/커널드라이버/일반서비스 ...)
   DWORD   dwCurrentState;                     // 서비스의 동작 상태
   DWORD   dwControlsAccepted;             // 서비스가 받을 수 있는 이벤트 설정 상태
   DWORD   dwWin32ExitCode;                  // 상태 변경주 에러가 발생했을 시 에러코드
   DWORD   dwServiceSpecificExitCode;    // 위와 같으나 좀 다르다.
   DWORD   dwCheckPoint;                       // 적당히 무시
   DWORD   dwWaitHint;                           // 적당히 무시
} SERVICE_STATUS, *LPSERVICE_STATUS;

그외에 설치할 때 당시의 여러 설정이나 상태를 변경하려면
ChangeServiceConfig 혹은 ChangeServiceConfig2  를 이용하여, 다양한 변경을 가해줄 수 있다.

서비스의 동작을 외부에서 제어했을 경우, 내부에서는 해당 상태에 맞도록 상태 설정을 처리해 주어야 한다.
(만약 내부에서 알맞게 상태를 변경하여 주지 않으면, 외부에서는 응답없음과 같은 기괴한 동작을 할것이다.)
SERVICE_STATUS ss;
ss.dwCurrentState = 원하는 상태;
SetServiceStatus(hSrv, &ss);

/* 설정 가능한 상태
#define SERVICE_STOPPED                        0x00000001
#define SERVICE_START_PENDING                  0x00000002
#define SERVICE_STOP_PENDING                   0x00000003
#define SERVICE_RUNNING                        0x00000004
#define SERVICE_CONTINUE_PENDING               0x00000005
#define SERVICE_PAUSE_PENDING                  0x00000006
#define SERVICE_PAUSED                         0x00000007
*/

저렇게 열어준 핸들은 사용후에 받드시 닫아주어야 한다.
CloseServiceHandle(hSrv);
CloseServiceHandle(hScm);

이전 장에서 서비스를 설치/제거, 여기서는 시작/중지를 확인하였으니, 다음 장에서는 서비스 본체에 대한 정리를 하고자 한다.
( 흐름을 보기 위한 부분으로 에러처리 없이 가장 간단한 형태로 설정이 되어 있으므로, 다음에는 추가적인 처리가 있을것이다.)

개념도 없이, 아무것도 없이 갑자기 웬? 서비스의 설치? 코딩 한줄도 못만들어 봤는데.. -_-???

작성한 서비스 프로그램을 돌려 보려면, 일단 서비스를 설치해서 구동해야 한다.
그냥 콘솔 상태에서 아무리 디버깅해봐야 실제로 서비스 레이어에서 구동할 때 정상적인지...
실제로 서비스로 돌아는 가는지 확인해 봐야 할것 아닌가?

일단 아주 심플한 서비스 설치용 함수이다.
인자 중에 NULL 라고 된것들은 첨에 알아봐야 머리만 아프고
별로 쓸일이 없는 경우이다. 예제 만들다 필요하면 설명이 추가될 수 도 있을것 같은데 잘 모르겠다.

해당 소스를 사용하려면
   헤더파일 : #include <windows.h>
   라이브러리 : Advapi32.lib
                   : 코드로 라이브러리를 추가하려면 #pragma comment(lib, "Advapi32.lib")
가 있어야 한다.

DWORD ServiceInstall()
{
    DWORD RET = ERROR_SUCCESS;
    SC_HANDLE hSrv;

    // SCM 을 열어서 서비스에 작업을 진행할 수 있도록 핸들을 하나 달라고 하자.
    // 지금 할 작업은 새로운 서비스를 만들어 등록하는 작업이다.
    SC_HANDLE hScm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
        
    // 열지 못하면 에러다. 머 어쩔 수 없다. 왜 그런지 에러 코드나 리턴한다.
    if (hScm == NULL)
        return GetLastError();
           
    // SCM 을 서비스 생성 권한으로 열었으니, 서비스를 만들어야지..
    // 함수에 필요한 인자들을 채워서 넣어주면 된다. 
    // 첫번째 인자는 SCM 에서 넘겨준 핸들이고 나머지는 밑에다가 추가적으로 설명을 단다.

    hSrv = CreateService(
        hScm,
        (1)서비스 이름,
        (2)서비스 표시 이름,
        (3)서비스 액세스 권한,
        (4)서버스 타입,
        (5)서비스 시작 타입,
        (6)서비스 에러 제어,
        (7)서비스 파일 경로,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL);
    
    // 서비스 생성이 실패하면 별수 없다. 여러가지 이유가 있을 테니까..
    // 에러 코드나 리턴하자.

    if (hSrv == NULL)
    {
        RET = GetLastError();
        CloseServiceHandle(hScm);
        return RET;
    }
    else
    {
        // 와우 서비스 생성에 성공했네...
        // 서비스 관리자 (services.msc) 를 열어보면 해당 서비스마다, 친절하게 이건 무신 무신 서비스 입니다.
        // 라는 설명을 보았을 것이다. 그와 같은 설명을 달아주는 부분이다.
        // 구지 설명 달아줄 필요가 없으면 건너 띄어도 무방하다.
        lpDes.lpDescription=(8)서비스의 세부 설명;
        if(!ChangeServiceConfig2(hSrv, SERVICE_CONFIG_DESCRIPTION, &lpDes))
        {
            RET = GetLastError();
            CloseServiceHandle(hScm);
            CloseServiceHandle(hSrv);
            return RET;
        }
        
        CloseServiceHandle(hScm);     
        return CloseServiceHandle(hSrv) ? ERROR_SUCCESS : GetLastError();
    }
}

+ Recent posts