- 윈도우즈 서비스 프로그램
Service Program 이란?
서비스 프로그램이란 윈도우즈에서 실행되는 프로그램들의 종류중 하나이며 다른 프로그램들과는 사용 용도나 개발 방법이 다르며 백그라운드에서 실행되는 프로그램이다.
서비스 프로그램
실제로 서비스 코드를 제공하는 프로그램, 백그라운드에서 사용자의 요구 또는 시스템의 변화를 감지하여 실제 작업을 처리하는 서비스 TCP/IP, NamedPipe등 원격지로부터 제어 신호처리도 가능하다.
서비스 설정 프로그램 ( Service Control Manager )
SCM은 설치된 서비스의 목록을 레지스트리에 DB형태로 저장하며 관리한다. 서비스를 시스템에 설치및 제거에 대한 작업을 수행하는 프로그램이며, 서비스 유형, 시작 유형, 설명 등 설치된 서비스의 여러가지 환경 설정을 조사하거나 수정할 수 있는 기능도 제공한다. 운영체제는 서비스를 설치해 주는 표준적인 방법을 제공하지 않으며 제어판의 서비스 애플릿도 서비스를 설치하는 기능을 제공하지 않으므로 설정 프로그램을 통해 설치 및 제거를 해야한다.
서비스 제어 프로그램
서비스는 백그라운드에서 사용자와 상호작용없이 동작하지만 사용자가 서비스를 제어할 수 있는 방법을 제공해 주어야 할 필요가 있다. 바이러스 체크 서비스의 경우 체크 방법을 변경한다거나 시스템에 바이러스가 있는지 조사해 보도록 직접 명령을 내릴 수도 있어야 한다. 또한 서비스를 일시 중지 시키거나 시작시킬수 있어야 한다. 그러나 서비스 자체는 사용자로부터 직접명령을 받을 수 있는 장치가 없다. 그래서 사용자로부터 명령을 받아 서비스에게 명령을 내리는 프로그램이 필요한데 이 프로그램을 서비스 제어 프로그램이라고 한다.
서비스 프로그램 제작의 세가지 요소
main 함수
서비스 프로그램은 사용자와 상호작용이 필요없기 때문에 보통 콘솔 프로세스로 작성된다.
GUI의 형태라면 WinMain도 가능하다.
main에서 해야할 가장 중요한 일은 Dispatcher쓰레드를 실행시키는 것이다.
Dispatcher이란?
프로세스와 프로세스에 속한 서비스들이 SCM과 통신을 하기위한 장치를 마련하는 것이다.
실행중인 서비스들은 SCM으로부터 끊임없이 제어 신호를 받아들여야 하고 자신의 상태를 SCM에게 보고해야하기 때문이다. Dispatcher는 서비스가 실행중인 동안 무한루프를 돌면서 서비스 시작과 제어신호를 전달한다.
BOOL StartServiceCtrlDispatcher( const LPSERVICE_TABLE_ENTRY lpServiceTable );
typedef struct _SERVICE_TABLE_ENTRY
{
LPTSTR lpServiceName; // 서비스 명칭
LPSERVICE_MAIN_FUNCTION lpServiceProc; // 서비스 메인 함수의 시작번지
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
lpServiceName은 서비스의 명칭이며, lpServiceProc는 서비스 메인 함수의 시작번지이다. 구조체 배열이므로 복수개의 서비스에 대한 정보를 전달할 수 있다. 단 배열의 끝을 표시하기 위해 마지막 요소는 NULL, NULL 값으로 되어 있어야 한다.
SCM과 프로세스를 연결하여준다. 성공하면 0이 아닌값을 리턴하며, 실패할 경우 0을 리턴한다.
SERVICE_TABLE_ENTRY ste[] = {
{"IPCTest", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
StateServiceCtrlDispatcher(ste);
ServiceMain
서비스메인은 실제 서비스 작업을 하는 서비스의 본체라고 할수 있다. 서비스메인은 main함수에서 등록되어 서비스를 실행시킬때 Dispatcher에 의해 호출되면서 서비스 운영에 관한 여러가지 일을 한다. 핸들러를 등록시키고 서비스를 가동시키며 자신의 상태변화를 SCM에게 알려준다.
void WINAPI ServiceMain( DWORD dwArgc, LPTSTR *lpszArgv )
핸들러 등록
서비스메인이 제일먼저 해야할 일은 서비스의 제어 신호를 처리하기 위한 핸들러를 등록하는 것이다. 서비스메인은 작업 스레드이며 메시지 루프를 가지지 않기 때문에 SCM으로부터 신호를 받을 수 있는 장치를 별도로 마련해야한다. 그것이 바로 핸들러이다. 이 작업은 다른 어떠한 것보다 우선적으로 처리 되어야 한다. 핸들러를 등록할때에는 RegisterServiceCtrlhandler이라는 함수를 사용한다.
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler(
_in LPCTSTR lpServiceName,
_in LPHANDLER_FUNCTION lpHandlerProc,
);
성공시에 서비스 상태의 핸들을 리턴한다.
실패시에 0을 리턴하며 GetLastError()함수로 정확히 확인할 수 있다.
상태보고
서비스는 실행중에 자신의 상태변화가 있을때마다 이를 SCM에게 보고해야 한다. 자신의 상태를 정확하게 보고해 주어야 SCM이 서비스가 어떤상태에 있는지 또한 어떠한 일을 하고 있는지 어떤 제어 신호를 받을 수 있는지 파악할 수 있다. 상태를 보고할 때에는 아래의 함수를 사용한다.
BOOL WINAPI SetServiceStatus(
_in SERVICE_STATUS_HANDLE hServiceStatus, // 서비스의 상태 핸들
_in LPSERVICE_STATUS lpServiceStatus // 현재 상태를 나타내는 구조체포인터
);
핸들러
핸들러 함수는 서비스의 제어신호를 처리하는 함수로서 서비스의 메시지 루프라고 보아도 좋다. 핸들러는 서비스 메인에서 Dispatcher에 등록된다.
서비스는 사용자로부터 직접 명령을 받지는 않지만 제어 프로그램이나 서비스 애플릿등으로부터 명령을 받아 처리한다. SCM이 이명령을 받아 Dispatcher로 전달하며 Dispatcher는 등록된 핸들러에게 제어 신호를 보내준다.
SCM이 보내는 제어 코드를 처리하는 함수이며, ServiceMain()함수에 의해 등록된다.
void WINAPI Handler(DWORD fdwControl)
핸들러의 등록
핸들러는 SCM으로부터 전달되는 제어 신호를 받아 처리하는 함수이다. 서비스는 실행중에 SCM과 끊임없는 통신을 수행하는데 SCM과의 통신방법으로 핸들러를 제공한다.
서비스메인에서 핸들러를 등록하려면 시스템은 핸들러의 번지를 보관하며 SCM으로부터 제어신호를 핸들러에게 전달해 준다.
핸들러 등록함수
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler( LPCTSTR lpServiceName, LPHANDLER_FUNCTION lpHandlerProc );
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerEx( LPCTSTR lpServiceName, LPHANDLE_FUNCTION_EX loHandlerProc, LPVOID lpContext )
Handler함수는 정수형 제어 신호값 하나만을 인수로 받아들이는데 비해 Ex함수는 더 많은 인수를 가진다. Ex함수는 Handler함수가 처리할 수 있는 신호보다 더 많은 복잡한 제어 신호를 받아들일 수 있는 장점이 있지만 2000 이전 버전과 호환이 되지 않는다. 두 핸들러중 어떤 타입을 사용하는가는 서비스의 기능과 형태에 따라서 선택할 수 있다. 서비스 실행중 전원변경이나 네트웍 구성변경에 대해서 까지 반응해야 할 경우 Ex함수를 사용한다.
제어 신호
핸들러 함수가 받을 수 있는 제어신호에는 다음과 같은 것들이 있다. 이 제어 신호는 1바이트의 정수값 이다.
신호 | 설 명 |
SERVICE_CONTROL_STOP |
서비스 중지 |
SERVICE_CONTROL_PAUSE | 서비스 일시중지 |
SERVICE_CONTROL_CONTINUE | 일시 중지된 서비스를 재개 |
SERVICE_CONTROL_INTERROGATE | 서비스의 현재 상태를 요청 |
SERVICE_CONTROL_SHUTDOWN | 시스템이 셧다운되므로 종료처리 하라 |
SERVICE_CONTROL_PARACHANGE | 서비스 시작 파라미터 변경 |
SERVICE_CONTROL_NETBINDADD | 바인딩 추가 |
SERVICE_CONTROL_NETBINDREMOVE | 바인딩 제거 |
SERVICE_CONTROL_NETBINDENABLE | 바인딩 사용가능 |
SERVICE_CONTROL_NETBINDDISABLE | 바인딩 사용불가 |
SERVICE_CONTROL_DEVICEEVENT | 디바이스 이벤트 통지 |
SERVICE_CONTROL_HARDWAREPROFILECHANGE | 하드웨어 프로파일 변화시킨 서비스 통지 |
SERVICE_CONTROL_POWEREVENT | 파워이벤트 통지 |
SERVICE_CONTROL_SESSIONCHANGE | 세션변화 이벤트 통지 |
서비스 메인과의 통신
핸들러는 어디까지나 신호를 받아들이는 일만 할뿐이지 서비스 본체의 코드를 가지고 있지 않기 때문에 자신이 직접 처리할수 없다. 서비스의 본체코드를 가지고 있는 서비스 메인만이 알고있기 때문이다. 그래서 핸들러는 신호를 받는 즉시 서비스 메인이 이 신호를 처리할 수 있도록 핸들러와 상호 통신할수 있어야 한다. 이 방법은 뮤텍스, 이벤트와 같은 동기화 객체를 이용하는 방법이 있으나 가장 간단한 방법으로 전역변수를 사용하는 방법이 있다.
******** QueryServiceStatus(
_in SC_HANDLE hService,
_out LPSERVICE_STATUS lpServiceStatus
); -> 서비스의 상태를 얻어온다.
출처 : http://csm3138.springnote.com/pages/1040096
'# IT, Computer Science > MFC, API' 카테고리의 다른 글
MFC 트레이 아이콘 등록 및 윈도우 감추기 (0) | 2010.09.07 |
---|---|
[MFC] OpenUrl을 이용하여 http소스 읽기 (0) | 2010.09.07 |
dialog간 변수 참조 (0) | 2010.08.14 |
내 컴퓨터의 아이피주소를 얻는 함수 (0) | 2010.07.25 |
MFC 다이얼로그에서 Enter키나 ECS키를 눌러 프로그램을 종료안되게 하기 (0) | 2010.04.14 |