//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include "service_helpers.h" static CRITICAL_SECTION g_CtrlHandlerMutex; static void (*g_pInternalServiceFn)( void *pParam ) = NULL; static void *g_pInternalServiceParam = NULL; static volatile bool g_bShouldExit = false; SERVICE_STATUS MyServiceStatus; SERVICE_STATUS_HANDLE MyServiceStatusHandle = NULL; void WINAPI MyServiceCtrlHandler( DWORD Opcode ) { DWORD status; switch(Opcode) { case SERVICE_CONTROL_STOP: // Do whatever it takes to stop here. ServiceHelpers_ExitEarly(); MyServiceStatus.dwWin32ExitCode = 0; MyServiceStatus.dwCurrentState = SERVICE_STOPPED; if ( !SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus) ) { status = GetLastError(); Msg( "[MY_SERVICE] SetServiceStatus error %ld\n", status ); } Msg( "[MY_SERVICE] Leaving MyService \n" ); return; case SERVICE_CONTROL_INTERROGATE: // Fall through to send current status. break; default: Msg("[MY_SERVICE] Unrecognized opcode %ld\n", Opcode ); } // Send current status. if ( !SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus ) ) { status = GetLastError(); Msg( "[MY_SERVICE] SetServiceStatus error %ld\n", status ); } } void WINAPI MyServiceStart( DWORD argc, LPTSTR *argv ) { DWORD status; MyServiceStatus.dwServiceType = SERVICE_WIN32; MyServiceStatus.dwCurrentState = SERVICE_START_PENDING; MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; MyServiceStatus.dwWin32ExitCode = 0; MyServiceStatus.dwServiceSpecificExitCode = 0; MyServiceStatus.dwCheckPoint = 0; MyServiceStatus.dwWaitHint = 0; MyServiceStatusHandle = RegisterServiceCtrlHandler( "MyService", MyServiceCtrlHandler ); if ( MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0 ) { Msg("[MY_SERVICE] RegisterServiceCtrlHandler failed %d\n", GetLastError() ); return; } // Initialization complete - report running status. MyServiceStatus.dwCurrentState = SERVICE_RUNNING; if ( !SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus ) ) { status = GetLastError(); Msg( "[MY_SERVICE] SetServiceStatus error %ld\n", status ); } // Run the app's main in-thread loop. g_pInternalServiceFn( g_pInternalServiceParam ); // Tell the SCM that we're stopped. MyServiceStatus.dwCurrentState = SERVICE_STOPPED; MyServiceStatus.dwWin32ExitCode = NO_ERROR; MyServiceStatus.dwServiceSpecificExitCode = 0; SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus ); // This is where the service does its work. Msg( "[MY_SERVICE] Returning the Main Thread \n" ); } void ServiceHelpers_Init() { InitializeCriticalSection( &g_CtrlHandlerMutex ); } bool ServiceHelpers_StartService( const char *pServiceName, void (*pFn)( void *pParam ), void *pParam ) { // Ok, just run the service. const SERVICE_TABLE_ENTRY DispatchTable[2] = { { (char*)pServiceName, MyServiceStart }, { NULL, NULL } }; g_pInternalServiceFn = pFn; g_pInternalServiceParam = pParam; if ( StartServiceCtrlDispatcher( DispatchTable ) ) { return true; } else { Msg( "StartServiceCtrlDispatcher error = '%s'\n", GetLastErrorString() ); return false; } } void ServiceHelpers_ExitEarly() { EnterCriticalSection( &g_CtrlHandlerMutex ); g_bShouldExit = true; LeaveCriticalSection( &g_CtrlHandlerMutex ); } bool ServiceHelpers_ShouldExit() { EnterCriticalSection( &g_CtrlHandlerMutex ); bool bRet = g_bShouldExit; LeaveCriticalSection( &g_CtrlHandlerMutex ); return bRet; } char* GetLastErrorString() { static char err[2048]; LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL ); strncpy( err, (char*)lpMsgBuf, sizeof( err ) ); LocalFree( lpMsgBuf ); err[ sizeof( err ) - 1 ] = 0; return err; }