////////////////////////////////////////////////////////////////////
// 아래의 구조체는 서비스에서 제공받는 이벤트를 시각적으로 출력할 때
// 사용하기 위하여 이벤트와 이름을 매핑하여 놓은 것이다.
////////////////////////////////////////////////////////////////////
typedef struct _STNAME
{
const char* text;
DWORD id;
} STNAME;
STNAME stname[] = {
{"", 0x00000000},
{"SERVICE_CONTROL_STOP", 0x00000001},
{"SERVICE_CONTROL_PAUSE", 0x00000002},
{"SERVICE_CONTROL_CONTINUE", 0x00000003},
{"SERVICE_CONTROL_INTERROGATE", 0x00000004},
{"SERVICE_CONTROL_SHUTDOWN", 0x00000005},
{"SERVICE_CONTROL_PARAMCHANGE", 0x00000006},
{"SERVICE_CONTROL_NETBINDADD", 0x00000007},
{"SERVICE_CONTROL_NETBINDREMOVE", 0x00000008},
{"SERVICE_CONTROL_NETBINDENABLE", 0x00000009},
{"SERVICE_CONTROL_NETBINDDISABLE", 0x0000000A},
{"SERVICE_CONTROL_DEVICEEVENT", 0x0000000B},
{"SERVICE_CONTROL_HARDWAREPROFILECHANGE", 0x0000000C},
{"SERVICE_CONTROL_POWEREVENT", 0x0000000D},
{"SERVICE_CONTROL_SESSIONCHANGE", 0x0000000E},
{"SERVICE_CONTROL_PRESHUTDOWN", 0x0000000F},
};
// 세션 체인지 이벤트에서는 하부 이벤트 타입을 아래 처럼 제공해준다.
STNAME wtsname[] = {
{"", 0x00000000},
{"CONSOLE_CONNECT", 0x00000001},
{"CONSOLE_DISCONNECT", 0x00000002},
{"REMOTE_CONNECT", 0x00000003},
{"REMOTE_DISCONNECT", 0x00000004},
{"LOGON", 0x00000005},
{"LOGOFF", 0x00000006},
{"LOCK", 0x00000007},
{"UNLOCK", 0x00000008},
{"SESSION_REMOTE_CONTROL", 0x00000009},
};
CServiceUtility::CSimpleLog FileLog("C:\\srv.log");
DWORD WINAPI service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
CServiceManager manager;
#ifdef _DEBUG
if(fdwControl != SERVICE_CONTROL_INTERROGATE)
{
fprintf(FileLog, "EVENT> %s\n", stname[fdwControl].text);
FileLog.flush();
}
#endif
switch (fdwControl)
{
case SERVICE_CONTROL_PAUSE:
manager.SetServiceRunStatus(SERVICE_PAUSE_PENDING);
// 서비스를 일시 중지 시킨다.
manager.SetServiceRunStatus(SERVICE_PAUSED);
break;
case SERVICE_CONTROL_CONTINUE:
manager.SetServiceRunStatus(SERVICE_CONTINUE_PENDING);
// 일시 중지 시킨 서비스를 재개한다.
manager.SetServiceRunStatus(SERVICE_RUNNING);
break;
case SERVICE_CONTROL_STOP:
manager.SetServiceRunStatus(SERVICE_STOP_PENDING);
// 서비스를 멈춘다 (즉, 종료와 같은 의미)
// 서비스를 종료하면, service_main 는 절대로 리턴하지 않는다.
// 그러므로 해제하려면 작업이 있으면 모든것을 이곳에서 처리한다.
manager.SetServiceRunStatus(SERVICE_STOPPED);
break;
// 윈도우에서 세션에 변경 사항이 발생하였을 경우, 발생하는 이벤트.
case SERVICE_CONTROL_SESSIONCHANGE:
{
// 세션에 관련된 이벤트 정보는 아래의 구조체에 복사하여 사용한다.
WTSSESSION_NOTIFICATION wtsnoti = {0};
memcpy(&wtsnoti, lpEventData, sizeof(WTSSESSION_NOTIFICATION));
// 유틸리티 CWTSSession 을 이용하여, 현재 검출된 세션의 정보를 읽어온다.
// 소스를 열어보면, 검출되는 정보에는 어떤것이 있는지 알 수 있다.
CServiceUtility::CWTSSession ws(WTS_CURRENT_SERVER_HANDLE, wtsnoti.dwSessionId);
// FileLog는 서비스는 결과를 화면에 출력할 수 없기 때문에, 아주 심플하게 파일로 찍고자 임시로 만듬.
// 이벤트 타입 dwEventType 를 이용하여 해당 이벤트때 필요한 작업을 하도록 처리해주면 된다.
// 현재는 정보만 출력해준다.
#ifdef _DEBUG
fprintf(FileLog, " WTS[%s <%d>] => IP(%s), Client(%s), Domain(%s), User(%s), WinStation(%s)",
wtsname[dwEventType].text,
wtsnoti.dwSessionId,
ws.IPAddress,
ws.ClientName,
ws.DomainName,
ws.UserName,
ws.WinStation);
FileLog.flush();
#endif
}
default:
break;
}
return NO_ERROR;
}