/****************************** Module Header ******************************\ * Module Name: ServiceBase.cpp * Project: CppWindowsService * Copyright (c) Microsoft Corporation. * * Provides a base class for a service that will exist as part of a service * application. CServiceBase must be derived from when creating a new service * class. * * This source is subject to the Microsoft Public License. * See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. * All other rights reserved. * * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. \***************************************************************************/ #pragma region Includes #include "ServiceBase.h" #include #include #pragma endregion #pragma region Static Members // Initialize the singleton service instance. CServiceBase *CServiceBase::s_service = nullptr; bool CServiceBase::Run(CServiceBase &service) { s_service = &service; SERVICE_TABLE_ENTRY serviceTable[] = { { service.m_name, ServiceMain }, { nullptr, nullptr } }; // Connects the main thread of a service process to the service control // manager, which causes the thread to be the service control dispatcher // thread for the calling process. This call returns when the service has // stopped. The process should simply terminate when the call returns. return StartServiceCtrlDispatcher(serviceTable) == TRUE; } void WINAPI CServiceBase::ServiceMain(DWORD argc, LPSTR *argv) { assert(s_service != nullptr); // Register the handler function for the service s_service->m_statusHandle = RegisterServiceCtrlHandler( s_service->m_name, ServiceCtrlHandler); if (s_service->m_statusHandle == nullptr) { throw GetLastError(); } // Start the service. s_service->Start(argc, argv); } // the control code can be one of the following values: // // SERVICE_CONTROL_CONTINUE // SERVICE_CONTROL_INTERROGATE // SERVICE_CONTROL_NETBINDADD // SERVICE_CONTROL_NETBINDDISABLE // SERVICE_CONTROL_NETBINDREMOVE // SERVICE_CONTROL_PARAMCHANGE // SERVICE_CONTROL_PAUSE // SERVICE_CONTROL_SHUTDOWN // SERVICE_CONTROL_STOP // // This parameter can also be a user-defined control code ranges from 128 // to 255. // void WINAPI CServiceBase::ServiceCtrlHandler(DWORD dwCtrl) { switch (dwCtrl) { case SERVICE_CONTROL_STOP: s_service->Stop(); break; case SERVICE_CONTROL_PAUSE: s_service->Pause(); break; case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break; case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break; case SERVICE_CONTROL_INTERROGATE: break; default: break; } } #pragma endregion #pragma region Service Constructor and Destructor // // FUNCTION: CServiceBase::CServiceBase(PWSTR, BOOL, BOOL, BOOL) // // PURPOSE: The constructor of CServiceBase. It initializes a new instance // of the CServiceBase class. The optional parameters (fCanStop, /// fCanShutdown and fCanPauseContinue) allow you to specify whether the // service can be stopped, paused and continued, or be notified when system // shutdown occurs. // // PARAMETERS: // * pszServiceName - the name of the service // * fCanStop - the service can be stopped // * fCanShutdown - the service is notified when system shutdown occurs // * fCanPauseContinue - the service can be paused and continued // CServiceBase::CServiceBase(char *serviceName, bool fCanStop, bool fCanShutdown, bool fCanPauseContinue) { // Service name must be a valid string and cannot be nullptr. m_name = (serviceName == nullptr) ? "" : serviceName; m_statusHandle = nullptr; // The service runs in its own process. m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // The service is starting. m_status.dwCurrentState = SERVICE_START_PENDING; // The accepted commands of the service. DWORD dwControlsAccepted = 0; if (fCanStop) dwControlsAccepted |= SERVICE_ACCEPT_STOP; if (fCanShutdown) dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; if (fCanPauseContinue) dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; m_status.dwControlsAccepted = dwControlsAccepted; m_status.dwWin32ExitCode = NO_ERROR; m_status.dwServiceSpecificExitCode = 0; m_status.dwCheckPoint = 0; m_status.dwWaitHint = 0; } // // FUNCTION: CServiceBase::~CServiceBase() // // PURPOSE: The virtual destructor of CServiceBase. // CServiceBase::~CServiceBase(void) { } #pragma endregion #pragma region Service Start, Stop, Pause, Continue, and Shutdown void CServiceBase::Start(DWORD dwArgc, LPSTR *lpszArgv) { try { // Tell SCM that the service is starting. SetServiceStatus(SERVICE_START_PENDING); // Perform service-specific initialization. OnStart(dwArgc, lpszArgv); // Tell SCM that the service is started. SetServiceStatus(SERVICE_RUNNING); } catch (DWORD dwError) { // Log the error. WriteErrorLogEntry("Service Start", dwError); // Set the service status to be stopped. SetServiceStatus(SERVICE_STOPPED, dwError); } catch (...) { // Log the error. WriteEventLogEntry("Service failed to start.", EVENTLOG_ERROR_TYPE); // Set the service status to be stopped. SetServiceStatus(SERVICE_STOPPED); } } // // FUNCTION: CServiceBase::Stop() // // PURPOSE: The function stops the service. It calls the OnStop virtual // function in which you can specify the actions to take when the service // stops. If an error occurs, the error will be logged in the Application // event log, and the service will be restored to the original state. // void CServiceBase::Stop() { DWORD dwOriginalState = m_status.dwCurrentState; try { // Tell SCM that the service is stopping. SetServiceStatus(SERVICE_STOP_PENDING); // Perform service-specific stop operations. OnStop(); // Tell SCM that the service is stopped. SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { // Log the error. WriteErrorLogEntry("Service Stop", dwError); // Set the original service status. SetServiceStatus(dwOriginalState); } catch (...) { // Log the error. WriteEventLogEntry("Service failed to stop.", EVENTLOG_ERROR_TYPE); // Set the original service status. SetServiceStatus(dwOriginalState); } } // // FUNCTION: CServiceBase::Pause() // // PURPOSE: The function pauses the service if the service supports pause // and continue. It calls the OnPause virtual function in which you can // specify the actions to take when the service pauses. If an error occurs, // the error will be logged in the Application event log, and the service // will become running. // void CServiceBase::Pause() { try { // Tell SCM that the service is pausing. SetServiceStatus(SERVICE_PAUSE_PENDING); // Perform service-specific pause operations. OnPause(); // Tell SCM that the service is paused. SetServiceStatus(SERVICE_PAUSED); } catch (DWORD dwError) { // Log the error. WriteErrorLogEntry("Service Pause", dwError); // Tell SCM that the service is still running. SetServiceStatus(SERVICE_RUNNING); } catch (...) { // Log the error. WriteEventLogEntry("Service failed to pause.", EVENTLOG_ERROR_TYPE); // Tell SCM that the service is still running. SetServiceStatus(SERVICE_RUNNING); } } // // FUNCTION: CServiceBase::Continue() // // PURPOSE: The function resumes normal functioning after being paused if // the service supports pause and continue. It calls the OnContinue virtual // function in which you can specify the actions to take when the service // continues. If an error occurs, the error will be logged in the // Application event log, and the service will still be paused. // void CServiceBase::Continue() { try { // Tell SCM that the service is resuming. SetServiceStatus(SERVICE_CONTINUE_PENDING); // Perform service-specific continue operations. OnContinue(); // Tell SCM that the service is running. SetServiceStatus(SERVICE_RUNNING); } catch (DWORD dwError) { // Log the error. WriteErrorLogEntry("Service Continue", dwError); // Tell SCM that the service is still paused. SetServiceStatus(SERVICE_PAUSED); } catch (...) { // Log the error. WriteEventLogEntry("Service failed to resume.", EVENTLOG_ERROR_TYPE); // Tell SCM that the service is still paused. SetServiceStatus(SERVICE_PAUSED); } } // // FUNCTION: CServiceBase::Shutdown() // // PURPOSE: The function executes when the system is shutting down. It // calls the OnShutdown virtual function in which you can specify what // should occur immediately prior to the system shutting down. If an error // occurs, the error will be logged in the Application event log. // void CServiceBase::Shutdown() { try { // Perform service-specific shutdown operations. OnShutdown(); // Tell SCM that the service is stopped. SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { // Log the error. WriteErrorLogEntry("Service Shutdown", dwError); } catch (...) { // Log the error. WriteEventLogEntry("Service failed to shut down.", EVENTLOG_ERROR_TYPE); } } #pragma region Helper Functions // * dwCurrentState - the state of the service // * dwWin32ExitCode - error code to report // * dwWaitHint - estimated time for pending operation, in milliseconds void CServiceBase::SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { static DWORD dwCheckPoint = 1; // Fill in the SERVICE_STATUS structure of the service. m_status.dwCurrentState = dwCurrentState; m_status.dwWin32ExitCode = dwWin32ExitCode; m_status.dwWaitHint = dwWaitHint; m_status.dwCheckPoint = ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) ? 0 : dwCheckPoint++; // Report the status of the service to the SCM. ::SetServiceStatus(m_statusHandle, &m_status); } // the type of event to be logged can be one of the following values: // // EVENTLOG_SUCCESS // EVENTLOG_AUDIT_FAILURE // EVENTLOG_AUDIT_SUCCESS // EVENTLOG_ERROR_TYPE // EVENTLOG_INFORMATION_TYPE // EVENTLOG_WARNING_TYPE // void CServiceBase::WriteEventLogEntry(const char *message, int wType) { HANDLE hEventSource = nullptr; LPCSTR lpszStrings[2] = { nullptr, nullptr }; hEventSource = RegisterEventSource(nullptr, m_name); if (hEventSource) { lpszStrings[0] = m_name; lpszStrings[1] = message; ReportEvent(hEventSource, // Event log handle wType, // Event type 0, // Event category 0, // Event identifier nullptr, // No security identifier 2, // Size of lpszStrings array 0, // No binary data lpszStrings, // Array of strings nullptr // No binary data ); DeregisterEventSource(hEventSource); } } void CServiceBase::WriteErrorLogEntry(const char *function, int error) { char message[260]; StringCchPrintf(message, ARRAYSIZE(message), "%s failed w/err 0x%08lx", function, error); WriteEventLogEntry(message, EVENTLOG_ERROR_TYPE); } #pragma endregion