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.
938 lines
34 KiB
938 lines
34 KiB
// --------------------------------------------------------------------------
|
|
// Module Name: Service.cpp
|
|
//
|
|
// Copyright (c) 2000, Microsoft Corporation
|
|
//
|
|
// This file contains a class that implements generic portions of a Win32
|
|
// serivce.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
#include "StandardHeader.h"
|
|
|
|
#define STRSAFE_LIB
|
|
#include <strsafe.h>
|
|
|
|
#include "Service.h"
|
|
|
|
#include "RegistryResources.h"
|
|
#include "StatusCode.h"
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::CService
|
|
//
|
|
// Arguments: pAPIConnection = CAPIConnection used to implement service.
|
|
// pServerAPI = CServerAPI used to stop service.
|
|
// pszServiceName = Name of service.
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Constructor for CService.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
CService::CService (CAPIConnection *pAPIConnection, CServerAPI *pServerAPI, const TCHAR *pszServiceName) :
|
|
_hService(NULL),
|
|
_pszServiceName(pszServiceName),
|
|
_pAPIConnection(pAPIConnection),
|
|
_pServerAPI(pServerAPI),
|
|
_pAPIDispatchSync(NULL)
|
|
{
|
|
CopyMemory(_szTag, CSVC_TAG, CB_CSVC_TAG);
|
|
|
|
ZeroMemory(&_serviceStatus, sizeof(_serviceStatus));
|
|
pAPIConnection->AddRef();
|
|
pServerAPI->AddRef();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::~CService
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Destructor for CService. Release used resources.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
CService::~CService (void)
|
|
|
|
{
|
|
CopyMemory(_szTag, DEAD_CSVC_TAG, CB_CSVC_TAG);
|
|
|
|
_pServerAPI->Release();
|
|
_pServerAPI = NULL;
|
|
_pAPIConnection->Release();
|
|
_pAPIConnection = NULL;
|
|
|
|
delete _pAPIDispatchSync;
|
|
_pAPIDispatchSync = NULL;
|
|
|
|
ASSERTMSG(_hService == NULL, "_hService should be released in CService::~CService");
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::IsValid
|
|
//
|
|
// Arguments: address of CService instance
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Reports whether the specified address points to a valid
|
|
// CService object.
|
|
//
|
|
// Found that there are cases when SCM launches a thread to
|
|
// interrogate the service (SERVICE_CONTROL_INTERROGATE) when
|
|
// trying to restart a CService who has already been deleted, but
|
|
// whose status has not yet gone from STOP_PENDING to STOPPED.
|
|
//
|
|
// This is unavoidable because SCM will dump the service process once
|
|
// STOPPED, not giving us a chance to delete the CService. Thus
|
|
// we assign STOPPED as late as possible.
|
|
//
|
|
// History: 2002-03-21 scotthan created
|
|
// --------------------------------------------------------------------------
|
|
BOOL CService::IsValid(CService* pService)
|
|
{
|
|
return pService ?
|
|
(0 == memcmp(pService->_szTag, CSVC_TAG, CB_CSVC_TAG)) :
|
|
FALSE;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::Start
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Called from ServiceMain of the service. This registers the
|
|
// handler and starts the service (listens to the API port).
|
|
// When the listen call returns it sets the status of the service
|
|
// as stopped and exits.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// 2002-03-21 scotthan add robustness.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CService::Start (void)
|
|
{
|
|
AddRef(); // defensive addref
|
|
|
|
_hService = RegisterServiceCtrlHandlerEx(_pszServiceName, CB_HandlerEx, this);
|
|
|
|
if (_hService != NULL)
|
|
{
|
|
NTSTATUS status;
|
|
BOOL fExit = FALSE;
|
|
ASSERTMSG(_pAPIDispatchSync == NULL, "CService::Start - _pAPIDispatchSync != NULL: reentered before shutdown\n");
|
|
|
|
_pAPIDispatchSync = new CAPIDispatchSync;
|
|
|
|
if( _pAPIDispatchSync != NULL )
|
|
{
|
|
_serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
|
|
_serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
_serviceStatus.dwCheckPoint = 0;
|
|
_serviceStatus.dwWaitHint = 0;
|
|
|
|
TSTATUS(SignalStartStop(TRUE));
|
|
|
|
// Add a reference for the HandlerEx callback. When the handler receives
|
|
// a stop code it will release its reference.
|
|
AddRef();
|
|
|
|
_serviceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
TBOOL(_SetServiceStatus(_hService, &_serviceStatus, this));
|
|
|
|
status = _pAPIConnection->Listen(_pAPIDispatchSync);
|
|
|
|
if( CAPIDispatchSync::WaitForServiceControlStop(
|
|
_pAPIDispatchSync, DISPATCHSYNC_TIMEOUT) != WAIT_TIMEOUT )
|
|
{
|
|
fExit = TRUE;
|
|
}
|
|
else
|
|
{
|
|
_serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
_serviceStatus.dwWin32ExitCode = ERROR_TIMEOUT;
|
|
|
|
DISPLAYMSG("CService::Start - Timed out waiting for SERVICE_CONTROL_STOP/SHUTDOWN.");
|
|
TBOOL(_SetServiceStatus(_hService, &_serviceStatus, this));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
_serviceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
|
|
|
|
TBOOL(_SetServiceStatus(_hService, &_serviceStatus, this));
|
|
}
|
|
}
|
|
|
|
Release(); // defensive addref
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::Install
|
|
//
|
|
// Arguments: pszName = Name of the service.
|
|
// pszImage = Executable image of the service.
|
|
// pszGroup = Group to which the service belongs.
|
|
// pszAccount = Account under which the service runs.
|
|
// pszDllName = Name of the hosting dll.
|
|
// pszDependencies = Any dependencies the service has.
|
|
// pszSvchostGroup = The svchost group.
|
|
// dwStartType = Start type of the service.
|
|
// hInstance = HINSTANCE for resources.
|
|
// uiDisplayNameID = Resource ID of the display name.
|
|
// uiDescriptionID = Resource ID of the description.
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Use the service control manager to create the service. Add
|
|
// additional information that CreateService does not allow us to
|
|
// directly specify and add additional information that is
|
|
// required to run in svchost.exe as a shared service process.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::Install (const TCHAR *pszName,
|
|
const TCHAR *pszImage,
|
|
const TCHAR *pszGroup,
|
|
const TCHAR *pszAccount,
|
|
const TCHAR *pszDllName,
|
|
const TCHAR *pszDependencies,
|
|
const TCHAR *pszSvchostGroup,
|
|
const TCHAR *pszServiceMainName,
|
|
DWORD dwStartType,
|
|
HINSTANCE hInstance,
|
|
UINT uiDisplayNameID,
|
|
UINT uiDescriptionID,
|
|
SERVICE_FAILURE_ACTIONS *psfa)
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = AddService(pszName, pszImage, pszGroup, pszAccount, pszDependencies, dwStartType, hInstance, uiDisplayNameID, psfa);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = AddServiceDescription(pszName, hInstance, uiDescriptionID);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = AddServiceParameters(pszName, pszDllName, pszServiceMainName);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = AddServiceToGroup(pszName, pszSvchostGroup);
|
|
}
|
|
}
|
|
}
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TSTATUS(Remove(pszName));
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::Remove
|
|
//
|
|
// Arguments: pszName = Name of service to remove.
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Use the service control manager to delete the service. This
|
|
// doesn't clean up the turds left for svchost usage.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::Remove (const TCHAR *pszName)
|
|
|
|
{
|
|
NTSTATUS status;
|
|
SC_HANDLE hSCManager;
|
|
|
|
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
|
if (hSCManager != NULL)
|
|
{
|
|
SC_HANDLE hSCService;
|
|
|
|
hSCService = OpenService(hSCManager, pszName, DELETE);
|
|
if (hSCService != NULL)
|
|
{
|
|
if (DeleteService(hSCService) != FALSE)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
status = CStatusCode::StatusCodeOfLastError();
|
|
}
|
|
TBOOL(CloseServiceHandle(hSCService));
|
|
}
|
|
else
|
|
{
|
|
status = CStatusCode::StatusCodeOfLastError();
|
|
}
|
|
TBOOL(CloseServiceHandle(hSCManager));
|
|
}
|
|
else
|
|
{
|
|
status = CStatusCode::StatusCodeOfLastError();
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::SignalStartStop
|
|
//
|
|
// Arguments: BOOL fStart
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Base class implementation of signal service started function.
|
|
// MUST BE INVOKED BY CHILD OVERRIDE!
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// 2002-03-11 scotthan renamed to 'SignalStartStop' from 'Signal',
|
|
// added boolean arg,
|
|
// 2002-03-24 scotthan added firing of 'stopping'notification
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::SignalStartStop (BOOL fStart)
|
|
{
|
|
if( !fStart )
|
|
{
|
|
CAPIDispatchSync::SignalServiceStopping(_pAPIDispatchSync);
|
|
}
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Default return codes for SCM control requests
|
|
typedef struct
|
|
{
|
|
DWORD dwControl;
|
|
DWORD dwRet;
|
|
} SERVICE_CONTROL_RETURN;
|
|
|
|
const SERVICE_CONTROL_RETURN _rgDefaultControlRet[] =
|
|
{
|
|
{ SERVICE_CONTROL_STOP, NO_ERROR},
|
|
{ SERVICE_CONTROL_SHUTDOWN, NO_ERROR},
|
|
{ SERVICE_CONTROL_INTERROGATE, NO_ERROR},
|
|
{ SERVICE_CONTROL_PAUSE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_CONTINUE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_PARAMCHANGE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_NETBINDADD, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_NETBINDREMOVE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_NETBINDENABLE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_NETBINDDISABLE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_DEVICEEVENT, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_HARDWAREPROFILECHANGE, ERROR_CALL_NOT_IMPLEMENTED},
|
|
{ SERVICE_CONTROL_POWEREVENT, ERROR_CALL_NOT_IMPLEMENTED},
|
|
};
|
|
|
|
// --------------------------------------------------------------------------
|
|
DWORD _GetDefaultControlRet(DWORD dwControl)
|
|
{
|
|
for(int i = 0; i < ARRAYSIZE(_rgDefaultControlRet); i++)
|
|
{
|
|
if( dwControl == _rgDefaultControlRet[i].dwControl )
|
|
return _rgDefaultControlRet[i].dwRet;
|
|
}
|
|
|
|
DISPLAYMSG("Unknown service control code passed to CService::CB_HandlerEx");
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::HandlerEx
|
|
//
|
|
// Arguments: dwControl = Control code from service control manager.
|
|
//
|
|
// Returns: DWORD
|
|
//
|
|
// Purpose: HandlerEx function for the service. The base class implements
|
|
// most of the useful things that the service will want to do.
|
|
// It's declared virtual in case overriding is required.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// 2002-03-21 scotthan Make shutdown more robust.
|
|
// --------------------------------------------------------------------------
|
|
|
|
DWORD CService::HandlerEx (DWORD dwControl)
|
|
|
|
{
|
|
DWORD dwErrorCode = _GetDefaultControlRet(dwControl);
|
|
SERVICE_STATUS_HANDLE hService = _hService;
|
|
|
|
if( hService != NULL )
|
|
{
|
|
switch (dwControl)
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
{
|
|
// In the stop/shutdown case, we do the following:
|
|
// (1) respond to the message by setting the status to SERVICE_STOP_PENDING.
|
|
// (2) Signal to all blocking LPC request handler threads that the service
|
|
// is coming down. This should cause them to terminate gracefully and
|
|
// come home.
|
|
// (3) Send an API_GENERIC_STOP request down the LPC port, telling it to quit.
|
|
// (this call can only succeed if it comes from within this process.)
|
|
// (4) Wait until the port finishes shutting down.
|
|
// (5) Signal to ServiceMain that the SERVICE_CONTROL_STOP/SHUTDOWN has completed.
|
|
// (6) ServiceMain exits.
|
|
|
|
// Step (1): update status to SERVICE_STOP_PENDING.
|
|
_serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
TBOOL(_SetServiceStatus(hService, &_serviceStatus, this));
|
|
|
|
// Step (2): Call home any waiting LPC request handler threads
|
|
SignalStartStop(FALSE);
|
|
|
|
// Step (3) Send an API_GENERIC_STOP LPC request to stop listening on
|
|
// the port (this will immediately release ServiceMain,
|
|
// who in turn needs to wait until we're completely finished here before exiting.)
|
|
NTSTATUS status;
|
|
TSTATUS((status = _pServerAPI->Stop()));
|
|
|
|
if( NT_SUCCESS(status) )
|
|
{
|
|
// Step (4): Wait until the API_GENERIC_STOP is finished.
|
|
if( CAPIDispatchSync::WaitForPortShutdown(
|
|
_pAPIDispatchSync, DISPATCHSYNC_TIMEOUT) != WAIT_TIMEOUT )
|
|
{
|
|
_serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
_serviceStatus.dwWin32ExitCode = CStatusCode::ErrorCodeOfStatusCode(status);
|
|
TBOOL(_SetServiceStatus(_hService, &_serviceStatus, this));
|
|
_hService = hService = NULL;
|
|
|
|
// Release reference on ourselves.
|
|
// The matching AddRef occurs in CService::Start.
|
|
Release();
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = ERROR_TIMEOUT;
|
|
DISPLAYMSG("CService::HandlerEx - Timed out waiting for port shutdown.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_serviceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
TBOOL(_SetServiceStatus(hService, &_serviceStatus, this));
|
|
dwErrorCode = CStatusCode::ErrorCodeOfStatusCode(status);
|
|
}
|
|
|
|
// Step (5): signal to ServiceMain that SERVICE_CONTROL_STOP/SHUTDOWN
|
|
// has completed; now he's safe to exit.
|
|
CAPIDispatchSync::SignalServiceControlStop(_pAPIDispatchSync);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// Report current status:
|
|
TBOOL(_SetServiceStatus(hService, &_serviceStatus, this));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(dwErrorCode);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService::CB_HandlerEx
|
|
//
|
|
// Arguments: See the platform SDK under HandlerEx.
|
|
//
|
|
// Returns: DWORD
|
|
//
|
|
// Purpose: Static function stub to call into the class.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// 2002-03-21 scotthan Add robustness.
|
|
// --------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI CService::CB_HandlerEx (DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(dwEventType);
|
|
UNREFERENCED_PARAMETER(lpEventData);
|
|
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
CService* pService = reinterpret_cast<CService*>(lpContext);
|
|
|
|
DEBUG_TRY();
|
|
|
|
if( CService::IsValid(pService) )
|
|
{
|
|
pService->AddRef(); // purely defensive; not until we call SetServiceStatus(SERVICE_STOPPED)
|
|
// will SCM stop calling into us via HandlerEx, so we need to ensure we stay alive.
|
|
dwRet = pService->HandlerEx(dwControl);
|
|
|
|
pService->Release(); // remove defensive AddRef().
|
|
}
|
|
else
|
|
{
|
|
DISPLAYMSG("CService::CB_HandlerEx - Warning: SCM control entrypoint invoked vs. invalid CService instance");
|
|
dwRet = _GetDefaultControlRet(dwControl);
|
|
}
|
|
|
|
DEBUG_EXCEPT("Breaking in CService::CB_HandlerEx exception handler");
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService:AddService
|
|
//
|
|
// Arguments: pszName = Name of the service.
|
|
// pszImage = Executable image of the service.
|
|
// pszGroup = Group to which the service belongs.
|
|
// pszAccount = Account under which the service runs.
|
|
// pszDependencies = Any dependencies the service has.
|
|
// dwStartType = Start type of the service.
|
|
// hInstance = HINSTANCE for resources.
|
|
// uiDisplayNameID = Resource ID of the display name.
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Uses the service control manager to create the service and add
|
|
// it into the database.
|
|
//
|
|
// History: 2000-12-09 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::AddService (const TCHAR *pszName,
|
|
const TCHAR *pszImage,
|
|
const TCHAR *pszGroup,
|
|
const TCHAR *pszAccount,
|
|
const TCHAR *pszDependencies,
|
|
DWORD dwStartType,
|
|
HINSTANCE hInstance,
|
|
UINT uiDisplayNameID,
|
|
SERVICE_FAILURE_ACTIONS *psfa)
|
|
|
|
{
|
|
DWORD dwErrorCode;
|
|
SC_HANDLE hSCManager;
|
|
|
|
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
|
if (hSCManager != NULL)
|
|
{
|
|
TCHAR sz[256];
|
|
|
|
if (LoadString(hInstance, uiDisplayNameID, sz, ARRAYSIZE(sz)) != 0)
|
|
{
|
|
SC_HANDLE hSCService;
|
|
|
|
hSCService = CreateService(hSCManager,
|
|
pszName,
|
|
sz,
|
|
SERVICE_ALL_ACCESS,
|
|
SERVICE_WIN32_SHARE_PROCESS,
|
|
dwStartType,
|
|
SERVICE_ERROR_NORMAL,
|
|
pszImage,
|
|
pszGroup,
|
|
NULL,
|
|
pszDependencies,
|
|
pszAccount,
|
|
NULL);
|
|
if (hSCService != NULL)
|
|
{
|
|
// Apply the failure action configuration, if any
|
|
if (psfa != NULL)
|
|
{
|
|
// If CreateService succeeded, why would this fail?
|
|
TBOOL(ChangeServiceConfig2(hSCService, SERVICE_CONFIG_FAILURE_ACTIONS, psfa));
|
|
}
|
|
|
|
TBOOL(CloseServiceHandle(hSCService));
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Blow off ERROR_SERVICE_EXISTS. If in the future the need
|
|
// to change the configuration arises add the code here.
|
|
|
|
dwErrorCode = GetLastError();
|
|
if (dwErrorCode == ERROR_SERVICE_EXISTS)
|
|
{
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
// Update service information for upgrade cases
|
|
hSCService = OpenService(hSCManager, pszName, SERVICE_ALL_ACCESS);
|
|
if (hSCService != NULL)
|
|
{
|
|
// Update the start type
|
|
TBOOL(ChangeServiceConfig(hSCService,
|
|
SERVICE_NO_CHANGE, // dwServiceType
|
|
dwStartType,
|
|
SERVICE_NO_CHANGE, // dwErrorControl
|
|
NULL, // lpBinaryPathName
|
|
NULL, // lpLoadOrderGroup
|
|
NULL, // lpdwTagId
|
|
NULL, // lpDependencies
|
|
NULL, // lpServiceStartName
|
|
NULL, // lpPassword
|
|
NULL // lpDisplayName
|
|
));
|
|
|
|
// Apply the failure action configuration, if any
|
|
if (psfa != NULL)
|
|
{
|
|
TBOOL(ChangeServiceConfig2(hSCService, SERVICE_CONFIG_FAILURE_ACTIONS, psfa));
|
|
}
|
|
|
|
TBOOL(CloseServiceHandle(hSCService));
|
|
}
|
|
}
|
|
}
|
|
TBOOL(CloseServiceHandle(hSCManager));
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
}
|
|
return(CStatusCode::StatusCodeOfErrorCode(dwErrorCode));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService:AddServiceDescription
|
|
//
|
|
// Arguments: pszName = Name of service.
|
|
// hInstance = HINSTANCE of module.
|
|
// uiDescriptionID = Resource ID of description.
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Reads the string resource from the given location and writes
|
|
// it as the description of the given service in the registry.
|
|
//
|
|
// History: 2000-12-09 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::AddServiceDescription (const TCHAR *pszName, HINSTANCE hInstance, UINT uiDescriptionID)
|
|
|
|
{
|
|
LONG lErrorCode;
|
|
TCHAR szKeyName[256];
|
|
CRegKey regKeyService;
|
|
|
|
if (!pszName || !pszName[0])
|
|
{
|
|
lErrorCode = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
StringCchCopy(szKeyName, ARRAYSIZE(szKeyName), TEXT("SYSTEM\\CurrentControlSet\\Services\\"));
|
|
StringCchCatN(szKeyName, ARRAYSIZE(szKeyName), pszName, (ARRAYSIZE(szKeyName) - 1) - lstrlen(szKeyName));
|
|
lErrorCode = regKeyService.Open(HKEY_LOCAL_MACHINE,
|
|
szKeyName,
|
|
KEY_SET_VALUE);
|
|
if (ERROR_SUCCESS == lErrorCode)
|
|
{
|
|
TCHAR sz[256];
|
|
|
|
if (LoadString(hInstance, uiDescriptionID, sz, ARRAYSIZE(sz)) != 0)
|
|
{
|
|
lErrorCode = regKeyService.SetString(TEXT("Description"), sz);
|
|
}
|
|
else
|
|
{
|
|
lErrorCode = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
return CStatusCode::StatusCodeOfErrorCode(lErrorCode);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService:AddServiceParameters
|
|
//
|
|
// Arguments: pszName = Name of service.
|
|
// pszDllName = Name of DLL hosting service.
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Adds parameters required for svchost to host this service.
|
|
//
|
|
// History: 2000-12-09 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::AddServiceParameters (const TCHAR* pszName, const TCHAR* pszDllName, const TCHAR* pszServiceMainName)
|
|
|
|
{
|
|
LONG lErrorCode;
|
|
TCHAR szKeyName[256];
|
|
CRegKey regKey;
|
|
|
|
// we handle a null pszServiceMainName
|
|
if (!pszName || !pszName[0] ||
|
|
!pszDllName || !pszDllName[0])
|
|
{
|
|
lErrorCode = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
StringCchCopy(szKeyName, ARRAYSIZE(szKeyName), TEXT("SYSTEM\\CurrentControlSet\\Services\\"));
|
|
StringCchCatN(szKeyName, ARRAYSIZE(szKeyName), pszName, (ARRAYSIZE(szKeyName) - 1) - lstrlen(szKeyName));
|
|
StringCchCatN(szKeyName, ARRAYSIZE(szKeyName), TEXT("\\Parameters"), (ARRAYSIZE(szKeyName) - 1) - lstrlen(szKeyName));
|
|
lErrorCode = regKey.Create(HKEY_LOCAL_MACHINE,
|
|
szKeyName,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE,
|
|
NULL);
|
|
if (ERROR_SUCCESS == lErrorCode)
|
|
{
|
|
TCHAR sz[256];
|
|
|
|
StringCchCopy(sz, ARRAYSIZE(sz), TEXT("%SystemRoot%\\System32\\"));
|
|
StringCchCatN(sz, ARRAYSIZE(sz), pszDllName, (ARRAYSIZE(sz) - 1) - lstrlen(sz));
|
|
lErrorCode = regKey.SetPath(TEXT("ServiceDll"), sz);
|
|
if (ERROR_SUCCESS == lErrorCode)
|
|
{
|
|
if (!pszServiceMainName || !pszServiceMainName[0])
|
|
{
|
|
StringCchCopy(sz, ARRAYSIZE(sz), TEXT("ServiceMain"));
|
|
pszServiceMainName = sz;
|
|
}
|
|
lErrorCode = regKey.SetString(TEXT("ServiceMain"), pszServiceMainName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return CStatusCode::StatusCodeOfErrorCode(lErrorCode);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService:AddServiceToGroup
|
|
//
|
|
// Arguments: pszName = Name of service.
|
|
// pszSvchostGroup = Group to which the service belongs.
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// Purpose: Adds the service as part of the group of services hosted in
|
|
// a single instance of svchost.exe.
|
|
//
|
|
// History: 2000-12-09 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
NTSTATUS CService::AddServiceToGroup (const TCHAR *pszName, const TCHAR *pszSvchostGroup)
|
|
|
|
{
|
|
LONG lErrorCode;
|
|
CRegKey regKey;
|
|
|
|
lErrorCode = regKey.Open(HKEY_LOCAL_MACHINE,
|
|
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost"),
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE);
|
|
if (ERROR_SUCCESS == lErrorCode)
|
|
{
|
|
DWORD dwType, dwBaseDataSize, dwDataSize;
|
|
|
|
dwType = dwBaseDataSize = dwDataSize = 0;
|
|
lErrorCode = regKey.QueryValue(pszSvchostGroup,
|
|
&dwType,
|
|
NULL,
|
|
&dwBaseDataSize);
|
|
if ((REG_MULTI_SZ == dwType) && (dwBaseDataSize != 0))
|
|
{
|
|
TCHAR *pszData;
|
|
|
|
dwDataSize = dwBaseDataSize + ((lstrlen(pszName) + 1) * sizeof(TCHAR));
|
|
pszData = static_cast<TCHAR*>(LocalAlloc(LMEM_FIXED, dwDataSize));
|
|
if (pszData != NULL)
|
|
{
|
|
lErrorCode = regKey.QueryValue(pszSvchostGroup,
|
|
NULL,
|
|
pszData,
|
|
&dwBaseDataSize);
|
|
if (ERROR_SUCCESS == lErrorCode)
|
|
{
|
|
if (*(pszData + (dwBaseDataSize / sizeof(TCHAR)) - 1) == L'\0')
|
|
{
|
|
if (!StringInMulitpleStringList(pszData, pszName))
|
|
{
|
|
StringInsertInMultipleStringList(pszData, pszName, dwDataSize);
|
|
lErrorCode = regKey.SetValue(pszSvchostGroup,
|
|
dwType,
|
|
pszData,
|
|
dwDataSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lErrorCode = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
(HLOCAL)LocalFree(pszData);
|
|
}
|
|
else
|
|
{
|
|
lErrorCode = ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lErrorCode = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
return(CStatusCode::StatusCodeOfErrorCode(lErrorCode));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService:StringInMulitpleStringList
|
|
//
|
|
// Arguments: pszStringList = String list to search.
|
|
// pszString = String to search for.
|
|
//
|
|
// Returns: bool
|
|
//
|
|
// Purpose: Searches the REG_MULTI_SZ string list looking for matches.
|
|
//
|
|
// History: 2000-12-01 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
bool CService::StringInMulitpleStringList (const TCHAR *pszStringList, const TCHAR *pszString)
|
|
|
|
{
|
|
bool fFound;
|
|
|
|
fFound = false;
|
|
while (!fFound && (pszStringList[0] != TEXT('\0')))
|
|
{
|
|
fFound = (lstrcmpi(pszStringList, pszString) == 0);
|
|
if (!fFound)
|
|
{
|
|
pszStringList += (lstrlen(pszStringList) + 1);
|
|
}
|
|
}
|
|
return(fFound);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CService:StringInsertInMultipleStringList
|
|
//
|
|
// Arguments: pszStringList = String list to insert string in.
|
|
// pszString = String to insert.
|
|
// cbStringListSize = Byte count of string list.
|
|
//
|
|
// Returns: bool
|
|
//
|
|
// Purpose: Inserts the given string into the multiple string list in
|
|
// the first alphabetical position encountered. If the list is
|
|
// kept alphabetical then this preserves it.
|
|
//
|
|
// History: 2000-12-02 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CService::StringInsertInMultipleStringList (TCHAR *pszStringList, const TCHAR *pszString, DWORD cbStringListSize)
|
|
|
|
{
|
|
int iResult, cchSize;
|
|
TCHAR *pszFirstString, *pszLastString;
|
|
|
|
pszFirstString = pszLastString = pszStringList;
|
|
cchSize = lstrlen(pszString) + 1;
|
|
iResult = -1;
|
|
while ((iResult < 0) && (pszStringList[0] != TEXT('\0')))
|
|
{
|
|
pszLastString = pszStringList;
|
|
iResult = lstrcmpi(pszStringList, pszString);
|
|
ASSERTMSG(iResult != 0, "Found exact match in StringInsertInMultipleStringList");
|
|
// 1 is for the '\0' terminator
|
|
pszStringList += (lstrlen(pszStringList) + 1);
|
|
}
|
|
if (iResult < 0)
|
|
{
|
|
pszLastString = pszStringList;
|
|
}
|
|
|
|
int cbLenToMove = cbStringListSize - (int(pszLastString - pszFirstString) * sizeof(TCHAR)) - (cchSize * sizeof(TCHAR));
|
|
|
|
if (cbLenToMove > 0) // Means that pszLastString + cchSize < pszFirstString + cbStringListSize
|
|
{
|
|
MoveMemory(pszLastString + cchSize, pszLastString, cbLenToMove);
|
|
StringCchCopy(pszLastString, cchSize, pszString);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CServiceWorkItem::CServiceWorkItem
|
|
//
|
|
// Arguments: pServerAPI = CServerAPI to use.
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Constructor for CServiceWorkItem.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
CServiceWorkItem::CServiceWorkItem (CServerAPI *pServerAPI) :
|
|
_pServerAPI(pServerAPI)
|
|
|
|
{
|
|
pServerAPI->AddRef();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CServiceWorkItem::~CServiceWorkItem
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Destructor for CServiceWorkItem. Release resources used.
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
CServiceWorkItem::~CServiceWorkItem (void)
|
|
|
|
{
|
|
_pServerAPI->Release();
|
|
_pServerAPI = NULL;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// CServiceWorkItem::Entry
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Executes work item request (stop the server).
|
|
//
|
|
// History: 2000-11-29 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CServiceWorkItem::Entry (void)
|
|
|
|
{
|
|
TSTATUS(_pServerAPI->Stop());
|
|
}
|
|
|