#include "stdafx.h"

CService::CService()
{
    m_evTerminate = NULL;
    m_evWakeUp = NULL;
    m_thHandle = NULL;
    m_bPauseService = FALSE;
    m_bRunningService = FALSE;
    m_StatusHandle = NULL;
}

CService::~CService()
{
    m_evTerminate = NULL;
    m_evWakeUp = NULL;
    m_thHandle = NULL;
    m_bPauseService = FALSE;
    m_bRunningService = FALSE;
    m_StatusHandle = NULL;
}

void __stdcall CService::HandlerThunk(CService* service, DWORD ctlcode)
{
    service->ServiceCtrlHandler(ctlcode);
}

DWORD __stdcall CService::HandlerExThunk(CService* service, DWORD ctlcode, DWORD evtype, PVOID evdata)
{
    switch (ctlcode) {

    case SERVICE_CONTROL_DEVICEEVENT:
    case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
        return service->ServiceHandleDeviceChange(evtype, (_DEV_BROADCAST_HEADER*) evdata);

    case SERVICE_CONTROL_POWEREVENT:
        return service->ServiceHandlePowerEvent(evtype, (DWORD) evdata);

    default:
        service->ServiceCtrlHandler(ctlcode);
        return NO_ERROR;
    }
}

void __stdcall CService::ServiceMainThunk(CService* service, DWORD argc, TCHAR* argv[])
{
    service->ServiceMain(argc, argv);
}

BOOL CService::InitService()
{
    DWORD id;

    m_thHandle = CreateThread(
                              NULL,
                              0,
                              (LPTHREAD_START_ROUTINE) ServiceThread,
                              (LPVOID)this,
                              0,
                              &id);

    if (m_thHandle == NULL) {
        PrintMessage("Cannot create thread");
        return FALSE;
    }
    m_bRunningService = TRUE;
    return TRUE;
}

DWORD WINAPI CService::ServiceThread(LPDWORD lParam)
{
    CService* service = (CService*)lParam;
    service->Run();
    return 0;
}

void CService::Run()
{
    BOOL res;

    while (1) {
        if (WaitForSingleObject(m_evWakeUp, 1000) == WAIT_OBJECT_0) {
           ResetEvent(m_evWakeUp);
           break;
        }
    }
}

BOOL CService::SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
{
    BOOL res;
    SERVICE_STATUS serviceStatus;

    serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
    serviceStatus.dwCurrentState = dwCurrentState;

    if (dwCurrentState == SERVICE_START_PENDING) {
        serviceStatus.dwControlsAccepted = 0;
    } else {
        serviceStatus.dwControlsAccepted =
                SERVICE_ACCEPT_STOP |
                SERVICE_ACCEPT_PAUSE_CONTINUE |
                SERVICE_ACCEPT_SHUTDOWN;
    }

    if (dwServiceSpecificExitCode == 0) {
        serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
    } else {
        serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
    }

    serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
    serviceStatus.dwCheckPoint = dwCheckPoint;
    serviceStatus.dwWaitHint = dwWaitHint;

    res = SetServiceStatus (m_StatusHandle, &serviceStatus);
    if (!res) {
        StopService();
    }

    return res;
}

void CService::ResumeService()
{
    m_bPauseService = FALSE;
    ResumeThread(m_thHandle);
}

void CService::PauseService()
{
    if (m_bRunningService && !m_bPauseService) {
        m_bPauseService = TRUE;
        SuspendThread(m_thHandle);
    }
}

void CService::StopService()
{
    if (m_bRunningService) {
        if (m_evWakeUp) {
           SetEvent(m_evWakeUp);
           if (WaitForSingleObject(m_thHandle, 1000) == WAIT_TIMEOUT) {
              TerminateThread(m_thHandle, 0);
           }
        }
        m_bRunningService = FALSE;
    }
    SetEvent(m_evTerminate);
}

void CService::terminate(DWORD error)
{
    if (m_evTerminate) {
        CloseHandle(m_evTerminate);
    }

    if (m_evWakeUp) {
        CloseHandle(m_evWakeUp);
    }

    DeleteCriticalSection(&m_scWrite);

    if (m_StatusHandle) {
        SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0);
    }

    if (m_thHandle) {
        CloseHandle(m_thHandle);
    }
}


void CService::ServiceCtrlHandler(DWORD controlCode)
{
    DWORD currentState = 0;

    switch(controlCode)
    {
        case SERVICE_CONTROL_STOP:
            currentState = SERVICE_STOP_PENDING;
            SendStatusToSCM(SERVICE_STOP_PENDING,
                            NO_ERROR,
                            0,
                            1,
                            5000);
            StopService();
            return;

        case SERVICE_CONTROL_PAUSE:
            if (m_bRunningService && !m_bPauseService) {
                SendStatusToSCM(SERVICE_PAUSE_PENDING,
                                NO_ERROR,
                                0,
                                1,
                                1000);

                PauseService();
                currentState = SERVICE_PAUSED;
            }
            break;

        case SERVICE_CONTROL_CONTINUE:
            if (m_bRunningService && m_bPauseService) {
                SendStatusToSCM(SERVICE_CONTINUE_PENDING,
                                NO_ERROR,
                                0,
                                1,
                                1000);

                ResumeService();
                currentState = SERVICE_RUNNING;
            }
            break;

        case SERVICE_CONTROL_INTERROGATE:
            break;

        case SERVICE_CONTROL_SHUTDOWN:
            return;

        default:
            break;
    }
    SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}

DWORD CService::ServiceHandleDeviceChange(DWORD evtype, _DEV_BROADCAST_HEADER* dbhdr)
{
    PrintMessage("ServiceHandleDeviceChange");
    return NO_ERROR;
}

DWORD CService::ServiceHandlePowerEvent(DWORD evtype, DWORD flags)
{
    PrintMessage("ServiceHandlePowerEvent");
    return NO_ERROR;
}

void CService::ServiceMain(DWORD argc, LPTSTR *argv)
{
    BOOL res;

    if (!m_StatusHandle) {
        terminate(GetLastError());
        return;
    }

    res = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 1, 5000);
    if (!res) {
        terminate(GetLastError());
        return;
    }

    m_evTerminate = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_evTerminate) {
        terminate(GetLastError());
        return;
    }

    m_evWakeUp = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_evWakeUp) {
        terminate(GetLastError());
        return;
    }

    InitializeCriticalSection(&m_scWrite);

    res = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 2, 1000);
    if (!res) {
        terminate(GetLastError());
        return;
    }

    res = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 3, 5000);
    if (!res) {
        terminate(GetLastError());
        return;
    }

    res = InitService();
    if (!res) {
        terminate(GetLastError());
        return;
    }

    res = SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);
    if (!res) {
        terminate(GetLastError());
        return;
    }

    WaitForSingleObject(m_evTerminate, INFINITE);
    terminate(0);
}


void CService::GetStatus(SC_HANDLE service)
{
    SERVICE_STATUS status;
    DWORD CurrentState;

    QueryServiceStatus(service, &status);

    switch(status.dwCurrentState) {
        case SERVICE_RUNNING:
            CurrentState = SERVICE_RUNNING;
            printf("Service RUNNING.\n");
            break;
        case SERVICE_STOPPED:
            CurrentState = SERVICE_STOPPED;
            printf("Service STOPPED.\n");
            break;
        case SERVICE_PAUSED:
            CurrentState = SERVICE_PAUSED;
            printf("Service PAUSED.\n");
            break;
        case SERVICE_CONTINUE_PENDING:
            CurrentState = SERVICE_CONTINUE_PENDING;
            printf("Service is resuming...\n");
            break;
        case SERVICE_PAUSE_PENDING:
            CurrentState = SERVICE_PAUSE_PENDING;
            printf("Service is pausing...\n");
            break;
        case SERVICE_START_PENDING:
            CurrentState = SERVICE_START_PENDING;
            printf("Service is starting...\n");
            break;
        case SERVICE_STOP_PENDING:
            CurrentState = SERVICE_STOP_PENDING;
            printf("Service is stopping...\n");
            break;
        default:
            break;
    }
    SendStatusToSCM(CurrentState, NO_ERROR, 0, 0, 0);
}
