123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /****************************** 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 <assert.h>
- #include <strsafe.h>
- #include <string>
- #pragma endregion
- #pragma region Static Members
- // Initialize the singleton service instance.
- CServiceBase *CServiceBase::s_service = NULL;
- //
- // FUNCTION: CServiceBase::Run(CServiceBase &)
- //
- // PURPOSE: Register the executable for a service with the Service Control
- // Manager (SCM). After you call Run(ServiceBase), the SCM issues a Start
- // command, which results in a call to the OnStart method in the service.
- // This method blocks until the service has stopped.
- //
- // PARAMETERS:
- // * service - the reference to a CServiceBase object. It will become the
- // singleton service instance of this service application.
- //
- // RETURN VALUE: If the function succeeds, the return value is TRUE. If the
- // function fails, the return value is FALSE. To get extended error
- // information, call GetLastError.
- //
- BOOL CServiceBase::Run(CServiceBase &service)
- {
- s_service = &service;
- SERVICE_TABLE_ENTRYA serviceTable[] =
- {
- { service.m_name, ServiceMain },
- { NULL, NULL }
- };
- // 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);
- }
- //
- // FUNCTION: CServiceBase::ServiceMain(DWORD, PWSTR *)
- //
- // PURPOSE: Entry point for the service. It registers the handler function
- // for the service and starts the service.
- //
- // PARAMETERS:
- // * dwArgc - number of command line arguments
- // * lpszArgv - array of command line arguments
- //
- void WINAPI CServiceBase::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
- {
- assert(s_service != NULL);
- // Register the handler function for the service
- s_service->m_statusHandle = RegisterServiceCtrlHandler(
- s_service->m_name, ServiceCtrlHandler);
- if (s_service->m_statusHandle == NULL)
- {
- throw GetLastError();
- }
- // Start the service.
- s_service->Start(dwArgc, pszArgv);
- }
- //
- // FUNCTION: CServiceBase::ServiceCtrlHandler(DWORD)
- //
- // PURPOSE: The function is called by the SCM whenever a control code is
- // sent to the service.
- //
- // PARAMETERS:
- // * dwCtrlCode - the control code. This parameter 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(PSTR pszServiceName,
- BOOL fCanStop,
- BOOL fCanShutdown,
- BOOL fCanPauseContinue)
- {
- // Service name must be a valid string and cannot be NULL.
- m_name = (pszServiceName == NULL) ? "" : pszServiceName;
- m_statusHandle = NULL;
- // 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
- //
- // FUNCTION: CServiceBase::Start(DWORD, PWSTR *)
- //
- // PURPOSE: The function starts the service. It calls the OnStart virtual
- // function in which you can specify the actions to take when the service
- // starts. If an error occurs during the startup, the error will be logged
- // in the Application event log, and the service will be stopped.
- //
- // PARAMETERS:
- // * dwArgc - number of command line arguments
- // * lpszArgv - array of command line arguments
- //
- void CServiceBase::Start(DWORD dwArgc, PSTR *pszArgv)
- {
- try
- {
- // Tell SCM that the service is starting.
- SetServiceStatus(SERVICE_START_PENDING);
- // Perform service-specific initialization.
- OnStart(dwArgc, pszArgv);
- // 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::OnStart(DWORD, PWSTR *)
- //
- // PURPOSE: When implemented in a derived class, executes when a Start
- // command is sent to the service by the SCM or when the operating system
- // starts (for a service that starts automatically). Specifies actions to
- // take when the service starts. Be sure to periodically call
- // CServiceBase::SetServiceStatus() with SERVICE_START_PENDING if the
- // procedure is going to take long time. You may also consider spawning a
- // new thread in OnStart to perform time-consuming initialization tasks.
- //
- // PARAMETERS:
- // * dwArgc - number of command line arguments
- // * lpszArgv - array of command line arguments
- //
- void CServiceBase::OnStart(DWORD dwArgc, PSTR *pszArgv)
- {
- }
- //
- // 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 orginal service status.
- SetServiceStatus(dwOriginalState);
- }
- catch (...)
- {
- // Log the error.
- WriteEventLogEntry("Service failed to stop.", EVENTLOG_ERROR_TYPE);
- // Set the orginal service status.
- SetServiceStatus(dwOriginalState);
- }
- }
- //
- // FUNCTION: CServiceBase::OnStop()
- //
- // PURPOSE: When implemented in a derived class, executes when a Stop
- // command is sent to the service by the SCM. Specifies actions to take
- // when a service stops running. Be sure to periodically call
- // CServiceBase::SetServiceStatus() with SERVICE_STOP_PENDING if the
- // procedure is going to take long time.
- //
- void CServiceBase::OnStop()
- {
- }
- //
- // 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::OnPause()
- //
- // PURPOSE: When implemented in a derived class, executes when a Pause
- // command is sent to the service by the SCM. Specifies actions to take
- // when a service pauses.
- //
- void CServiceBase::OnPause()
- {
- }
- //
- // 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::OnContinue()
- //
- // PURPOSE: When implemented in a derived class, OnContinue runs when a
- // Continue command is sent to the service by the SCM. Specifies actions to
- // take when a service resumes normal functioning after being paused.
- //
- void CServiceBase::OnContinue()
- {
- }
- //
- // 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);
- }
- }
- //
- // FUNCTION: CServiceBase::OnShutdown()
- //
- // PURPOSE: When implemented in a derived class, executes when the system
- // is shutting down. Specifies what should occur immediately prior to the
- // system shutting down.
- //
- void CServiceBase::OnShutdown()
- {
- }
- #pragma endregion
- #pragma region Helper Functions
- //
- // FUNCTION: CServiceBase::SetServiceStatus(DWORD, DWORD, DWORD)
- //
- // PURPOSE: The function sets the service status and reports the status to
- // the SCM.
- //
- // PARAMETERS:
- // * 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);
- }
- //
- // FUNCTION: CServiceBase::WriteEventLogEntry(PWSTR, WORD)
- //
- // PURPOSE: Log a message to the Application event log.
- //
- // PARAMETERS:
- // * pszMessage - string message to be logged.
- // * wType - the type of event to be logged. The parameter 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(PSTR pszMessage, WORD wType)
- {
- HANDLE hEventSource = NULL;
- LPCSTR lpszStrings[2] = { NULL, NULL };
- hEventSource = RegisterEventSource(NULL, m_name);
- if (hEventSource)
- {
- lpszStrings[0] = m_name;
- lpszStrings[1] = pszMessage;
- ReportEvent(hEventSource, // Event log handle
- wType, // Event type
- 0, // Event category
- 0, // Event identifier
- NULL, // No security identifier
- 2, // Size of lpszStrings array
- 0, // No binary data
- lpszStrings, // Array of strings
- NULL // No binary data
- );
- DeregisterEventSource(hEventSource);
- }
- }
- //
- // FUNCTION: CServiceBase::WriteErrorLogEntry(PWSTR, DWORD)
- //
- // PURPOSE: Log an error message to the Application event log.
- //
- // PARAMETERS:
- // * pszFunction - the function that gives the error
- // * dwError - the error code
- //
- void CServiceBase::WriteErrorLogEntry(PSTR pszFunction, DWORD dwError)
- {
- char szMessage[260];
- StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
- "%s failed w/err 0x%08lx", pszFunction, dwError);
- WriteEventLogEntry(szMessage, EVENTLOG_ERROR_TYPE);
- }
- #pragma endregion
|