*서비스프로그래밍
-메인이있긴하나 메인이메인이아니다.
-함수호출하지않고 콜백함수가있다.
-제약이 많지만 권한이 시스템 권한이다. << 이것이 강점!
-UI를 만들수가 없다.
*골치아픈 고려사항
- 서비스프로세스 <-> OS는 통신에 문제가없다. 그런데..
- 일반App. <- 서비스프로세스 (문제없음)
일반App. -> 서비스프로세스 (고민해야함)
*프로그램작성할때 고려할사항
- 서비스 install/uninstall/load/unload하는것을 다 만들어서 테스트해주어야한다.
*서비스 프로그램 소스 예제
///////////////////////////////////////////////////////
//header files start
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <TCHAR.h>
//header files end
///////////////////////////////////////////////////////
//define start
#define SERVICE_NAME _T("ServiceSample")
#define EXIT_EVENT _T("SAMPLE_SERVICE_EXIT_EVENT")
//define end
///////////////////////////////////////////////////////
//Global variable declaration start
SERVICE_STATUS_HANDLE g_hServiceStatus = NULL;
DWORD g_dwServiceState = 0;
HANDLE g_hEventExit = NULL;
//Global variable declaration end
///////////////////////////////////////////////////////
//function prototype declaration start
void TestServiceMain(DWORD argc, LPTSTR* argv);
void SampleServiceHandler(DWORD dwOpcode);
void LogPrint(LPTSTR pszLog, WORD wEventType);
void SetSampleServiceStatus(DWORD dwState, DWORD dwError, DWORD dwExError,
DWORD dwAccept = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE);
//마지막 dwAccept의 의미는 저러한 이벤트를 받을것이다.를 알림
//function prototype declaration end
///////////////////////////////////////////////////////
//Main function start
int main(int argc, char* argv[])
{
SERVICE_TABLE_ENTRY DispatchTable[] = {
{
SERVICE_NAME,
(LPSERVICE_MAIN_FUNCTION)TestServiceMain
},
{
NULL,NULL
}
};
//호출되는순간 서비스가 책임진다.얘는 더이상권한없음
if( !::StartServiceCtrlDispatcher(DispatchTable)){
}
return 0;
}
//main function end
///////////////////////////////////////////////////////////
//user funcion define start
// TestServiceMain
void TestServiceMain(DWORD argc, LPTSTR* argv){
//Steps
//1. 서비스 핸들러 등록
DWORD dwResult = 0;
g_hServiceStatus = ::RegisterServiceCtrlHandler(SERVICE_NAME,
(LPHANDLER_FUNCTION)SampleServiceHandler);
if( g_hServiceStatus == NULL){ //error
//event viewer에 에러 로그가 남도록 해주어야함!
LogPrint(_T("Failed to register Service control handler"),
EVENTLOG_ERROR_TYPE); //이벤트 뷰어에 남길모양
//WINNT.H에있음, AUDIT prefix : 보안관련
return;
}
//2.서비스가 시작되었음을 알리고 초기화 작업 시작
SetSampleServiceStatus(SERVICE_START_PENDING, NO_ERROR, 0);
//SERVICE_START_PENDING : 서비스 시작 중임.
//종료이벤트 설정 - 이건 에러가 나면 안된다.
g_hEventExit = ::CreateEvent(NULL,FALSE,FALSE,EXIT_EVENT);
if(g_hEventExit == NULL){ //문제생김
LogPrint( _T("Failed to create Exit event"),EVENTLOG_ERROR_TYPE);
return;
}
//TODO: 초기화 코드를 넣으세요 ㅋㅋ
//3.서비스가 정상적으로 실행되었음을 표시
SetSampleServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
LogPrint( _T("정상적으로 서비스가 등록됨."),EVENTLOG_INFORMATION_TYPE);
//4.종료이벤트를 기다림
::WaitForSingleObject(g_hEventExit,INFINITE);
//5. 서비스 프로세스를 종료함.
::CloseHandle(g_hEventExit);
SetSampleServiceStatus(SERVICE_STOPPED,NO_ERROR,0);
}
// SampleServiceHandler
void SampleServiceHandler(DWORD dwOpcode){
//서비스 매니저와 소통하는역할. 즉. 서비스매니저에의해 호출당함
if(dwOpcode == g_dwServiceState){
return;
}
switch(dwOpcode){
case SERVICE_CONTROL_PAUSE:
SetSampleServiceStatus(SERVICE_PAUSE_PENDING, NO_ERROR, 0);
//TODO: 일시정지시 관련 코드를 작성
SetSampleServiceStatus(SERVICE_PAUSED, NO_ERROR, 0);
LogPrint( _T("서비스가 일시 정지됨."),EVENTLOG_INFORMATION_TYPE);
break;
case SERVICE_CONTROL_CONTINUE:
SetSampleServiceStatus(SERVICE_CONTINUE_PENDING, NO_ERROR, 0);
//TODO: 재시작시 관련 코드를 작성
SetSampleServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
LogPrint( _T("서비스가 정상적으로 재시작됨."),EVENTLOG_INFORMATION_TYPE);
break;
case SERVICE_CONTROL_STOP:
SetSampleServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
//종료 과정이 시작됨
::SetEvent(g_hEventExit);
break;
case SERVICE_CONTROL_INTERROGATE:
SetSampleServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
break;
default:
SetSampleServiceStatus(g_dwServiceState, NO_ERROR, 0);
break;
}
}
// LogPrint
void LogPrint(LPTSTR pszLog, WORD wEventType){
TCHAR* lpszStrings[2] = {0};
TCHAR szMsg[256] = {0};
HANDLE hEventSource = NULL;
DWORD dwError = ::GetLastError();
//이벤트 소스를 등록한다. 서비스를 이벤트뷰어에등록하는거임
hEventSource = ::RegisterEventSource(NULL, SERVICE_NAME);
wsprintf(szMsg,_T("%s [ERROR CODE: %d]"),SERVICE_NAME,dwError);
lpszStrings[0] = szMsg;
lpszStrings[1] = pszLog;
if(hEventSource != NULL){
::ReportEvent(hEventSource,
wEventType,
0,
0,
NULL,
2,
0,
(const char**)(LPTSTR*)lpszStrings,
NULL);
::DeregisterEventSource(hEventSource);
}
}
// SetSampleServiceStatus
void SetSampleServiceStatus(DWORD dwState, DWORD dwError,DWORD dwExError, DWORD dwAccept){
SERVICE_STATUS ss = {0};
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = dwState;
ss.dwControlsAccepted = dwAccept;
ss.dwWin32ExitCode = dwError;
ss.dwServiceSpecificExitCode = 0;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 2000; //ms단위
if(dwError == ERROR_SERVICE_SPECIFIC_ERROR){
ss.dwServiceSpecificExitCode = dwExError;
}
g_dwServiceState = dwState;
::SetServiceStatus(g_hServiceStatus, &ss);
}
//user function define end
//////////////////////////////////////////////////////////////
댓글