#include "StdAfx.h"
#include "systemservice.h"
#include "04WebServer.h"
#include "Registry04.h"
#include "EventLog.h"



CSystemService::CSystemService(void)
{
	m_wantStop = 0;
}

CSystemService::~CSystemService(void)
{
}


//	サービス実行開始
int CSystemService::RunService()
{
	StartThread();

	//	STAのCOMのためにメッセージループをまわす
	MSG msg;
	while (m_wantStop == 0)
	{
		if(::PeekMessage(&msg, 0, 0, 0,PM_REMOVE))
		{
			if(msg.message == WM_QUIT)
				break;

			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
		else
			Sleep(50);
	}
	return(0);
}


//	サービス制御用スレッド
void CSystemService::ThreadMain()
{
	//	サービスを登録
	SERVICE_TABLE_ENTRY	serviceTable[] = {
	{ SERVICE_NAME, _ServiceMain},
	{ NULL,			 NULL		}};

	::StartServiceCtrlDispatcher(serviceTable);

	//	サービス停止までここには来ないはず
}


//	サービスメイン
void WINAPI CSystemService::ServiceMain()
{
	//	まずは、サービスハンドラを設定する
	m_serviceHandle = ::RegisterServiceCtrlHandlerEx(SERVICE_NAME,_HandlerEx,(PVOID)this);

	//	COM初期化
	CoInitialize(NULL);

	//	サーバー実行開始
	if(GET_APP()->StartServer())
		WriteSystemLog(MSG_ERROR,GET_APP()->GetServerErrorInfo(),1);	//	エラー報告、サービスは起動しないと、設定できない
	else
		WriteSystemLog(MSG_START);										//	起動ログ

	//	SCMにサーバ実行開始を通知
	SetServiceStatus(SERVICE_RUNNING);
	
	//	停止要求が来るまで、ループ
	while (m_wantStop == 0)
			Sleep(50);

	//	終了
	GET_APP()->StopServer();
	
	//	COM終了
	CoUninitialize();

	//	終了を通知
	SetServiceStatus(SERVICE_STOPPED);

	//	終了ログ
	WriteSystemLog(MSG_STOP);
}


//	サービスコントロールハンドラ
DWORD CSystemService::HandlerEx(DWORD dwControl,DWORD dwEventType,PVOID pvEventData,PVOID pvContext)
{
	switch(dwControl)
	{
		//	停止要求
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			m_wantStop = 1;
			SetServiceStatus(SERVICE_STOP_PENDING,0,0);
			return(NO_ERROR);
	};

	return(ERROR_CALL_NOT_IMPLEMENTED);
}


//	サービスステータスを更新
void CSystemService::SetServiceStatus(DWORD dwCurrentStat,DWORD error,DWORD dwCheckPoint,DWORD dwWaitHint)
{
	SERVICE_STATUS	status;
	
	status.dwServiceType				= SERVICE_WIN32_OWN_PROCESS;
	status.dwCurrentState				= dwCurrentStat;
	status.dwControlsAccepted			= SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
	status.dwWaitHint					= dwWaitHint;
	status.dwCheckPoint					= dwCheckPoint;

	if(error)
	{
		status.dwWin32ExitCode				= ERROR_SERVICE_SPECIFIC_ERROR ;
		status.dwServiceSpecificExitCode	= error;
	}
	else
	{
		status.dwWin32ExitCode				= NO_ERROR;
		status.dwServiceSpecificExitCode	= 0;
	}


	::SetServiceStatus(m_serviceHandle,&status);
}


//////////////////////////////////////////////////////////////////////////////////////////////////
//	関数ポインタ用
//////////////////////////////////////////////////////////////////////////////////////////////////
//	サービス本体(呼び出しのみ)
void WINAPI CSystemService::_ServiceMain(DWORD dwArgc,PSTR *pszArgv)
{
	GET_APP()->GetSystemService()->ServiceMain();
}

//	サービスコントロールハンドラ(呼び出しのみ)
DWORD WINAPI CSystemService::_HandlerEx(DWORD dwControl,DWORD dwEventType,PVOID pvEventData,PVOID pvContext)
{
	return(((CSystemService*)pvContext)->HandlerEx(dwControl,dwEventType,pvEventData,pvContext));
}


//////////////////////////////////////////////////////////////////////////////////////////////////
//	サービスユーティリティ
//////////////////////////////////////////////////////////////////////////////////////////////////
//	サービスの追加
int CSystemService::AddToService()
{
	//	まず削除
	DelFromService();

	SC_HANDLE	scm = ::OpenSCManager(NULL,SERVICES_ACTIVE_DATABASE,GENERIC_WRITE);
	if(scm == NULL)
		return(-1);

	CString	appPath;
	::GetModuleFileName(NULL,appPath.GetBuffer(_MAX_PATH+1),_MAX_PATH);
	appPath.ReleaseBuffer();

	//	スペースを排除
	if(appPath.Find(" "))
		appPath =  "\"" + appPath + "\"";
	appPath += "/Service";

	//	追加
	SC_HANDLE	add =	::CreateService(
										scm,
										SERVICE_NAME,
										SERVICE_DISPLAY_NAME,
										SERVICE_CHANGE_CONFIG,		//	情報設定
										SERVICE_WIN32_OWN_PROCESS,	//	1つのサービスを持つプロセス
										SERVICE_AUTO_START	,		//	自動実行
										SERVICE_ERROR_NORMAL,		//	エラーを記録
										appPath,					//	パス
										NULL,						//	システム初期の最後にロードされる
										NULL,
										NULL,						//	依存関係なし
										NULL,						//	ユーザーなし
										NULL);						//	〃


	//	情報設定
	SERVICE_DESCRIPTION desc;
	desc.lpDescription = _T(SERVICE_DESCRIPTION_STR);
	::ChangeServiceConfig2(add, SERVICE_CONFIG_DESCRIPTION, &desc);

	CloseServiceHandle(add);
	CloseServiceHandle(scm);

	return(0);
}

//	サービスの削除
int CSystemService::DelFromService()
{
	//	まずは停止する

	SC_HANDLE	scm = ::OpenSCManager(NULL,SERVICES_ACTIVE_DATABASE,SC_MANAGER_CONNECT);
	if(scm == NULL)
		return(-1);

	SC_HANDLE	service = ::OpenService(scm,SERVICE_NAME,DELETE | SERVICE_STOP);
	if(service == NULL)
	{
		CloseServiceHandle(scm);
		return(0);	//	無いっぽい
	}

	//	停止
	SERVICE_STATUS stat;
	QueryServiceStatus(service,&stat);
	if(stat.dwCurrentState != SERVICE_STOPPED)
	{
		ControlService(service,SERVICE_CONTROL_STOP,&stat);
	}

	//	削除
	if(!::DeleteService(service))
	{
		CloseServiceHandle(service);
		CloseServiceHandle(scm);
		return(-1);
	}

	CloseServiceHandle(service);
	CloseServiceHandle(scm);

	return(0);
}

//	イベントログ
void CSystemService::WriteSystemLog(int event,CString info,int isError)
{
	CRegistry04	reg;
	reg.Open(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\"SERVICE_NAME"\\");
	reg.SetValue("EventMessageFile",GET_APP()->GetAppPath(0));
	reg.SetValue("TypesSupported",(DWORD)EVENTLOG_INFORMATION_TYPE | EVENTLOG_ERROR_TYPE);
	reg.Close();


	HANDLE	eventSorce = ::RegisterEventSource(NULL,SERVICE_NAME);
	if(eventSorce == NULL)
		return;

	LPCSTR	string;
	int		stringCount = 0;

	if(!info.IsEmpty())
	{
		string = info.GetBuffer(0);
		stringCount = 1;
	}

	if(isError == 0)
		::ReportEvent(eventSorce,EVENTLOG_INFORMATION_TYPE,0,event,NULL,stringCount,0,&string,NULL);
	else
		::ReportEvent(eventSorce,EVENTLOG_ERROR_TYPE      ,0,event,NULL,stringCount,0,&string,NULL);

	::CloseHandle(eventSorce);
}

