/****************************************************************
copyright   : Copyright (C) 2011-2012, chenbichao,
All rights reserved.
filename    : pgSrcWin.cpp
discription : 
modify      : create, chenbichao, 2012/10/10

*****************************************************************/
#include "StdAfx.h"
#include "pgSvcWin.h"

#define LOG_FILE_PATH "pgSvcWin.log"


void pgSvcWinLog(const char* lpszFmt, ...)
{
	FILE *pFile = fopen(LOG_FILE_PATH, "a+");
	if (pFile != 0) {
		va_list args;
		va_start(args, lpszFmt);
		char szBuf[1024] = {0};
		int iSize = _vsnprintf(szBuf, sizeof(szBuf), lpszFmt, args);
		if (iSize > 0 && ((unsigned int)iSize + 3) < sizeof(szBuf)) {
			szBuf[iSize] = '\r';
			szBuf[iSize + 1] = '\n';
			szBuf[iSize + 2] = '\0';
			fprintf(pFile, "%s", szBuf);
		}
		va_end(args);
		fclose(pFile);
	}
}

static int UnicodeToUTF8(LPCTSTR lpszUnicode, char* lpszChars, unsigned int uSize)
{
	const wchar_t* pwsSrc = (const wchar_t*)lpszUnicode;
	unsigned int uWCLen = wcslen(pwsSrc);

	int iMBCLen = ::WideCharToMultiByte(CP_UTF8, 0, pwsSrc, uWCLen, 0, 0, 0, 0);
	if ((int)uSize > iMBCLen) {
		iMBCLen = ::WideCharToMultiByte(CP_UTF8, 0, pwsSrc, uWCLen, lpszChars, uSize, 0, 0);
		lpszChars[iMBCLen] = '\0';
	}
	else {
		iMBCLen = 0;
		lpszChars[iMBCLen] = '\0';
	}

	return iMBCLen;
}

static CPGSvcWin* s_ptheSvc = 0;

void CPGSvcWin::Run()
{
	OnRun();
}

void CPGSvcWin::Init(LPCTSTR lpszSvcName)
{
	char szSvcName[128] = {0};
	UnicodeToUTF8(lpszSvcName, szSvcName, sizeof(szSvcName));
	pgSvcWinLog("SvcWin::Init. szSvcName=%s", szSvcName);

	_tcscpy(m_szSvcName, lpszSvcName);

    // Set up the initial service status 
    m_hSvcStatus = NULL;
    m_stStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    m_stStatus.dwCurrentState = SERVICE_STOPPED;
    m_stStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    m_stStatus.dwWin32ExitCode = 0;
    m_stStatus.dwServiceSpecificExitCode = 0;
    m_stStatus.dwCheckPoint = 0;
    m_stStatus.dwWaitHint = 0;

    SERVICE_TABLE_ENTRY st[] = {
        {m_szSvcName, _ServiceMain},
        {NULL, NULL}
    };
    if (!::StartServiceCtrlDispatcher(st)) {
		pgSvcWinLog("SvcWin::Init. StartServiceCtrlDispatcher. iErr=%d", ::GetLastError());
		OnRun();
    }
}

BOOL CPGSvcWin::IsInstalled(LPCTSTR lpszSvcName)
{
	BOOL bResult = FALSE;
	SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM != NULL) {
		SC_HANDLE hService = ::OpenService(hSCM, lpszSvcName, SERVICE_ALL_ACCESS);
		if (hService != NULL) {
			TCHAR tzPath[_MAX_PATH + 128] = {0};
			if (::GetModuleFileName(NULL, tzPath, 256)) {
				
				// Add arguments
				_tcscat(tzPath, _T(" "));
				_tcscat(tzPath, lpszSvcName);

				DWORD dwByteNeed = 0;
				BYTE byBuf[1024] = {0};
				LPQUERY_SERVICE_CONFIG pstCfg = (LPQUERY_SERVICE_CONFIG)byBuf;
				if (QueryServiceConfig(hService, pstCfg, sizeof(byBuf), &dwByteNeed)) {

					if (_tcsicmp(pstCfg->lpBinaryPathName, tzPath) == 0) {
						bResult = TRUE;
					}
					else {
						pgSvcWinLog("SvcWin::IsInstalled, Remove old service.");
						SERVICE_STATUS status;
						::ControlService(hService, SERVICE_CONTROL_STOP, &status);

						unsigned int uInd = 0;
						while (uInd < 8) {
							SERVICE_STATUS_PROCESS ssStatus;
							if (!::QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO,
								(LPBYTE)&ssStatus, sizeof(ssStatus), &dwByteNeed))
							{
								break;
							}
							if (ssStatus.dwCurrentState != SERVICE_STOP_PENDING) {
								break;
							}
							::Sleep(1000);
							uInd++;
						}

						::DeleteService(hService);
					}
				}
				else {
					pgSvcWinLog("SvcWin::IsInstalled, QueryServiceConfig failed. iErr=%d", ::GetLastError());
				}
			}
			else {
				pgSvcWinLog("SvcWin::IsInstalled, GetModuleFileName failed. iErr=%d", ::GetLastError());
			}

			::CloseServiceHandle(hService);
		}
		else {
			pgSvcWinLog("SvcWin::IsInstalled, OpenService failed. iErr=%d", ::GetLastError());
		}

		::CloseServiceHandle(hSCM);
	}
	else {
		pgSvcWinLog("SvcWin::IsInstalled, OpenSCManager failed. iErr=%d", ::GetLastError());
	}

	return bResult;
}

BOOL CPGSvcWin::Install(LPCTSTR lpszSvcName, LPCTSTR lpszSvcCmmt, BOOL bAutoStart)
{
	char szSvcName[128] = {0};
	UnicodeToUTF8(lpszSvcName, szSvcName, sizeof(szSvcName));
	pgSvcWinLog("SvcWin::Install: szSvcName=%s", szSvcName);

	if (IsInstalled(lpszSvcName)) {
		pgSvcWinLog("SvcWin::Install, Has installed");
		return TRUE;
	}

	SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL) {
		pgSvcWinLog("SvcWin::Install, Couldn't open service manager. iErr=%d", ::GetLastError());
		return FALSE;
	}

	// Get the executable file path
	TCHAR szFilePath[_MAX_PATH + 128];
	if (!::GetModuleFileName(NULL, szFilePath, _MAX_PATH)) {
		pgSvcWinLog("SvcWin::Install, Get exe path failed. iErr=%d", ::GetLastError());
		return FALSE;
	}

	// Add arguments
	_tcscat(szFilePath, _T(" "));
	_tcscat(szFilePath, lpszSvcName);

	SC_HANDLE hService = ::CreateService(hSCM, lpszSvcName,
		lpszSvcCmmt, SERVICE_ALL_ACCESS, 
		(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS),
		(bAutoStart ? SERVICE_AUTO_START : SERVICE_DEMAND_START),
		SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL, NULL, NULL, NULL);
	if (hService == NULL) {
		::CloseServiceHandle(hSCM);
		pgSvcWinLog("SvcWin::Install, Couldn't create service. iErr=%d", ::GetLastError());
		return FALSE;
	}

	::CloseServiceHandle(hService);
	::CloseServiceHandle(hSCM);

	pgSvcWinLog("SvcWin::Install, Service install success.");
	return TRUE;
}

BOOL CPGSvcWin::Uninstall(LPCTSTR lpszSvcName)
{
	char szSvcName[128] = {0};
	UnicodeToUTF8(lpszSvcName, szSvcName, sizeof(szSvcName));
	pgSvcWinLog("SvcWin::Uninstall: szSvcName=%s", szSvcName);

	if (!IsInstalled(lpszSvcName)) {
		pgSvcWinLog("SvcWin::Uninstall, It is not installed");
		return TRUE;
	}

	SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL) {
		pgSvcWinLog("SvcWin::Uninstall: Couldn't open service manager. iErr=%d", ::GetLastError());
		return FALSE;
	}

	SC_HANDLE hService = ::OpenService(hSCM, lpszSvcName, SERVICE_STOP | DELETE);
	if (hService == NULL) {
		::CloseServiceHandle(hSCM);
		pgSvcWinLog("SvcWin::Uninstall: Couldn't open service. iErr=%d", ::GetLastError());
		return FALSE;
	}
	SERVICE_STATUS status;
	::ControlService(hService, SERVICE_CONTROL_STOP, &status);

	BOOL bDelete = ::DeleteService(hService);
	::CloseServiceHandle(hService);
	::CloseServiceHandle(hSCM);
	if (!bDelete) {
		pgSvcWinLog("SvcWin::Uninstall: Service could not be deleted. iErr=%d", ::GetLastError());
		return FALSE;
	}

	pgSvcWinLog("SvcWin::Uninstall: Success.");
	return TRUE;
}

BOOL CPGSvcWin::Start(LPCTSTR lpszSvcName)
{
	char szSvcName[128] = {0};
	UnicodeToUTF8(lpszSvcName, szSvcName, sizeof(szSvcName));
	pgSvcWinLog("SvcWin::Start: szSvcName=%s", szSvcName);

	if (!IsInstalled(lpszSvcName)) {
		pgSvcWinLog("SvcWin::Start: Service not exist");
		return FALSE;
	}

	SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCM == NULL) {
		pgSvcWinLog("SvcWin::Start: Couldn't open service manager. iErr=%d", ::GetLastError());
		return FALSE;
	}

	SC_HANDLE hService = ::OpenService(hSCM, lpszSvcName, SERVICE_ALL_ACCESS);
	if (hService == NULL) {
		::CloseServiceHandle(hSCM);
		pgSvcWinLog("SvcWin::Start: Couldn't open service. iErr=%d", ::GetLastError());
		return FALSE;
	}

	BOOL bStart = ::StartService(hService, 0, 0);
	::CloseServiceHandle(hService);
	::CloseServiceHandle(hSCM);
	if (!bStart) {
		pgSvcWinLog("SvcWin::Start: Service can not be started. iErr=%d", ::GetLastError());
		return FALSE;
	}

	pgSvcWinLog("SvcWin::Start: Service success.");
	return TRUE;
}

void CPGSvcWin::SetServiceStatus(DWORD dwState)
{
	m_stStatus.dwCurrentState = dwState;
	::SetServiceStatus(m_hSvcStatus, &m_stStatus);
}

CPGSvcWin::CPGSvcWin()
{
	s_ptheSvc = this;
}

CPGSvcWin::~CPGSvcWin()
{
	s_ptheSvc = 0;
}


void CPGSvcWin::ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
	char szSvcName[128] = {0};
	UnicodeToUTF8(m_szSvcName, szSvcName, sizeof(szSvcName));
	pgSvcWinLog("SvcWin::ServiceMain: szSvcName=%s", szSvcName);

	// Register the control request handler
	m_stStatus.dwCurrentState = SERVICE_START_PENDING;
	m_hSvcStatus = RegisterServiceCtrlHandler(m_szSvcName, _Handler);
	if (m_hSvcStatus == NULL) {
		pgSvcWinLog("SvcWin::ServiceMain, Handler not installed. iErr=%d", ::GetLastError());
		return;
	}

	SetServiceStatus(SERVICE_START_PENDING);
	m_stStatus.dwWin32ExitCode = S_OK;
	m_stStatus.dwCheckPoint = 0;
	m_stStatus.dwWaitHint = 0;

	// When the Run function returns, the service has stopped.
	SetServiceStatus(SERVICE_RUNNING);

	pgSvcWinLog("SvcWin::ServiceMain, Service running");
	OnRun();

	SetServiceStatus(SERVICE_STOPPED);

	pgSvcWinLog("SvcWin::ServiceMain, Service stopped");
}

void CPGSvcWin::Handler(DWORD dwOpcode)
{
	switch (dwOpcode) {
	case SERVICE_CONTROL_STOP:
		SetServiceStatus(SERVICE_STOP_PENDING);
		pgSvcWinLog("SvcWin::Handler, Service stop pending.");
		OnExit();
		break;
	case SERVICE_CONTROL_PAUSE:
		break;
	case SERVICE_CONTROL_CONTINUE:
		break;
	case SERVICE_CONTROL_INTERROGATE:
		break;
	case SERVICE_CONTROL_SHUTDOWN:
		break;
	default:
		pgSvcWinLog("Bad service request");
	}
}

void WINAPI CPGSvcWin::_ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
	if (s_ptheSvc != 0) {
		s_ptheSvc->ServiceMain(dwArgc, lpszArgv);
	}
}

void WINAPI CPGSvcWin::_Handler(DWORD dwOpcode)
{
	if (s_ptheSvc != 0) {
		s_ptheSvc->Handler(dwOpcode);
	}
}

