You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
563 lines
16 KiB
563 lines
16 KiB
/****************************** 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 |