Programming/윈도우 서비스
[서비스] 뼈다귀 서비스 코드 [7/?]
까막백
2009. 10. 15. 10:28
지난 내용
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]
[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 [6/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 Cont. [6/?]
이전 장에서 다룬 서비스 본체 코딩이라고 한 부분은 유틸성 클래스를 이용하여, 본체를 구성하여 보았다.
다음 주제로 넘어가기 이전에, 잡다한거 다 제거하고 순수하게.. API 만으로 작성한 코드를 살펴보면서
차이점이 무엇인지? 구지 저런거 다 넣고 진행할 필요가 있는지. 급할 때는 기본 코드만으로 쓱~~ ^^;;;
아래의 코드는 이전 샘플과 거의 동일한 기능을 제공한다. 그리고, 실제로도 대부분 저렇게 구성하는 경우가 많다.
#include <windows.h>
#include <winsvc.h>
#define SRVNAME "CROWBACK Service"
SERVICE_STATUS_HANDLE hss = NULL;
SERVICE_STATUS ss = {SERVICE_WIN32_OWN_PROCESS, 0, 0xFF, 0, 0, 0, 0};
DWORD GetServiceRunStatus(SERVICE_STATUS& ss);
DWORD GetServiceRunStatus();
DWORD WINAPI service_handler(DWORD fdwCtrl, DWORD dwEvent, LPVOID lpData, LPVOID lParam);
int service_main(int argc, char** argv);
// 1. 원래의 메인 함수
int main(int argc, char** argv)
{
SERVICE_TABLE_ENTRY STE[] =
{
{SRVNAME, (LPSERVICE_MAIN_FUNCTION)service_main},
{NULL,NULL}
};
if(StartServiceCtrlDispatcher(STE) == FALSE)
return -1;
return 0;
}
// 2. 서비스의 메인 함수
int service_main(int argc, char** argv)
{
hss = RegisterServiceCtrlHandlerEx(SRVNAME, service_handler, NULL);
if (hss == NULL)
return -1;
ss.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(hss, &ss);
// bla bla...
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hss, &ss);
while(GetServiceRunStatus() != SERVICE_STOPPED)
{
if(GetServiceRunStatus() == SERVICE_PAUSED)
{
Sleep(1000);
continue;
}
::MessageBeep(0xFFFFFFFF);
Sleep(1000);
}
return 0;
}
// 3. 이벤트 핸들러 함수.
DWORD WINAPI service_handler(DWORD fdwCtrl, DWORD dwEvent, LPVOID lpData, LPVOID lParam)
{
switch (fdwCtrl)
{
case SERVICE_CONTROL_PAUSE:
ss.dwCurrentState = SERVICE_PAUSE_PENDING;
SetServiceStatus(hss, &ss);
// to do
ss.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(hss, &ss);
break;
case SERVICE_CONTROL_CONTINUE:
ss.dwCurrentState = SERVICE_CONTINUE_PENDING;
SetServiceStatus(hss, &ss);
// to do
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hss, &ss);
break;
case SERVICE_CONTROL_STOP:
ss.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(hss, &ss);
// to do
ss.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hss, &ss);
break;
default:
break;
}
return NO_ERROR;
}
// 4. 서비스의 상태를 읽어오는 함수.
DWORD GetServiceRunStatus(SERVICE_STATUS& ss)
{
SC_HANDLE hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(hScm == NULL)
return ERROR_INVALID_HANDLE;
SC_HANDLE hSrv = OpenService(hScm, SRVNAME, SERVICE_INTERROGATE);
if (hSrv == NULL)
return GetLastError();
if(ControlService(hSrv, SERVICE_CONTROL_INTERROGATE, &ss) == 0)
{
DWORD dw = GetLastError();
if(dw == ERROR_SERVICE_NOT_ACTIVE)
{
ss.dwCurrentState = SERVICE_STOPPED;
return ERROR_SUCCESS;
}
return dw;
}
CloseServiceHandle(hSrv);
CloseServiceHandle(hScm);
return ERROR_SUCCESS;
}
DWORD GetServiceRunStatus()
{
SERVICE_STATUS ss = {0};
if(GetServiceRunStatus(ss) != ERROR_SUCCESS)
return ERROR_SUCCESS;
return ss.dwCurrentState;
}
#include <winsvc.h>
#define SRVNAME "CROWBACK Service"
SERVICE_STATUS_HANDLE hss = NULL;
SERVICE_STATUS ss = {SERVICE_WIN32_OWN_PROCESS, 0, 0xFF, 0, 0, 0, 0};
DWORD GetServiceRunStatus(SERVICE_STATUS& ss);
DWORD GetServiceRunStatus();
DWORD WINAPI service_handler(DWORD fdwCtrl, DWORD dwEvent, LPVOID lpData, LPVOID lParam);
int service_main(int argc, char** argv);
// 1. 원래의 메인 함수
int main(int argc, char** argv)
{
SERVICE_TABLE_ENTRY STE[] =
{
{SRVNAME, (LPSERVICE_MAIN_FUNCTION)service_main},
{NULL,NULL}
};
if(StartServiceCtrlDispatcher(STE) == FALSE)
return -1;
return 0;
}
// 2. 서비스의 메인 함수
int service_main(int argc, char** argv)
{
hss = RegisterServiceCtrlHandlerEx(SRVNAME, service_handler, NULL);
if (hss == NULL)
return -1;
ss.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(hss, &ss);
// bla bla...
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hss, &ss);
while(GetServiceRunStatus() != SERVICE_STOPPED)
{
if(GetServiceRunStatus() == SERVICE_PAUSED)
{
Sleep(1000);
continue;
}
::MessageBeep(0xFFFFFFFF);
Sleep(1000);
}
return 0;
}
// 3. 이벤트 핸들러 함수.
DWORD WINAPI service_handler(DWORD fdwCtrl, DWORD dwEvent, LPVOID lpData, LPVOID lParam)
{
switch (fdwCtrl)
{
case SERVICE_CONTROL_PAUSE:
ss.dwCurrentState = SERVICE_PAUSE_PENDING;
SetServiceStatus(hss, &ss);
// to do
ss.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(hss, &ss);
break;
case SERVICE_CONTROL_CONTINUE:
ss.dwCurrentState = SERVICE_CONTINUE_PENDING;
SetServiceStatus(hss, &ss);
// to do
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hss, &ss);
break;
case SERVICE_CONTROL_STOP:
ss.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(hss, &ss);
// to do
ss.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hss, &ss);
break;
default:
break;
}
return NO_ERROR;
}
// 4. 서비스의 상태를 읽어오는 함수.
DWORD GetServiceRunStatus(SERVICE_STATUS& ss)
{
SC_HANDLE hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(hScm == NULL)
return ERROR_INVALID_HANDLE;
SC_HANDLE hSrv = OpenService(hScm, SRVNAME, SERVICE_INTERROGATE);
if (hSrv == NULL)
return GetLastError();
if(ControlService(hSrv, SERVICE_CONTROL_INTERROGATE, &ss) == 0)
{
DWORD dw = GetLastError();
if(dw == ERROR_SERVICE_NOT_ACTIVE)
{
ss.dwCurrentState = SERVICE_STOPPED;
return ERROR_SUCCESS;
}
return dw;
}
CloseServiceHandle(hSrv);
CloseServiceHandle(hScm);
return ERROR_SUCCESS;
}
DWORD GetServiceRunStatus()
{
SERVICE_STATUS ss = {0};
if(GetServiceRunStatus(ss) != ERROR_SUCCESS)
return ERROR_SUCCESS;
return ss.dwCurrentState;
}
위의 소스를 빌드하면, 바로 서비스 프로그램이 생성된다. 단, 저건 스스로 설치할 수 없다, 스스로 제거할 수 도 없다.
외부에서 해당 서비스의 이름을 가지고, 이전 장에서 주욱 나열해왔던 설치부터 실행 제거까지 전부 외부에 의존해야 한다.
이전에 설명했던거 다 필요없이 저거면 서비스 프로그램이 되는 것이다. 급할 때 헤딩하지 말고, Ctrl+C, Ctrl+V 신공을 발휘하여
빠르게 진행할 때 사용하기 위해서, 만들어둔 템플리트 처럼 사용하고 있는 코드이기도 하다.