windows-nt/Source/XPSP1/NT/com/svcdlls/trksvcs/common/svcctrl.cxx
2020-09-26 16:20:57 +08:00

302 lines
8.8 KiB
C++

// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: svcctrl.cxx
//
// Contents: Class for service control interface.
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "trklib.hxx"
#define THIS_FILE_NUMBER SVCCTRL_CXX_FILE_NO
// This is static so that we can handle a PNP timing problem
// (see the comment in CSvcCtrlInterface::ServiceHandler).
BOOL CSvcCtrlInterface::_fStoppedOrStopping = TRUE;
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::Initialize
//
// Register our service control handler with the control dispatcher, and set our state
// to start-pending.
//
//+----------------------------------------------------------------------------
void
CSvcCtrlInterface::Initialize(const TCHAR *ptszServiceName, IServiceHandler *pServiceHandler)
{
_fInitializeCalled = TRUE;
_pServiceHandler = pServiceHandler;
_fStoppedOrStopping = FALSE;
_dwCheckPoint = 0;
// Register with the control dispatcher.
_ssh = RegisterServiceCtrlHandlerEx(ptszServiceName, ServiceHandler, this );
if (_ssh == 0)
{
TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, HRESULT_FROM_WIN32(GetLastError()),
ptszServiceName );
TrkRaiseLastError();
}
// Go to the start-pending state.
SetServiceStatus(SERVICE_START_PENDING, 0, NO_ERROR);
}
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::ServiceHandler
//
// This method is called by the control dispatcher. If we get a stop
// or shutdown request, automatically send a stop-pending before calling
// the service's handler. Interrogate is handled automatically in
// this routine without bothering to call the service.
//
//+----------------------------------------------------------------------------
DWORD // static
CSvcCtrlInterface::ServiceHandler(DWORD dwControl,
DWORD dwEventType,
PVOID EventData,
PVOID pData)
{
// NOTE: In services.exe, this method is called on the one and only ServiceHandler
// thread. So while we execute, no other service in this process can
// receive notifications. Thus it is important that we do nothing
// blocking or time-consuming here.
DWORD dwRet = NO_ERROR;
CSvcCtrlInterface *pThis = (CSvcCtrlInterface*)pData;
#if DBG
if( SERVICE_CONTROL_STOP == dwControl )
TrkLog(( TRKDBG_SVR|TRKDBG_WKS, TEXT("\n") ));
TrkLog(( TRKDBG_SVR|TRKDBG_WKS,
TEXT("ServiceHandler(%s)"),
StringizeServiceControl(dwControl) ));
#endif
// On a stop or shutdown, flag it (e.g. so we don't try to accept new
// requests from clients) and tell the SCM that we're stopping.
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
pThis->SetServiceStatus(SERVICE_STOP_PENDING, 0, NO_ERROR);
_fStoppedOrStopping = TRUE;
break;
}
// Check for PNP timing issues. The problem is that during a stop
// or shutdown, we unregister with PNP so that we don't get any more
// notifications. This is fine, except that between now and the time
// we do that unregister, more PNP notifications might get queued. So
// when we get called to process those undesired notifications, we need
// to ignore them here in the static function.
//
// As a quick fix, since only trkwks receives PNP notifications, we'll
// just check to see if it's alive. A better fix (raided) is to have
// a static function for each service, so that only the trkwks has to
// deal with it.
if( SERVICE_CONTROL_DEVICEEVENT == dwControl && _fStoppedOrStopping )
{
TrkLog(( TRKDBG_WARNING, TEXT("Ignoring SERVICE_CONTROL_DEVICEEVENT; service is stopped") ));
return dwRet;
}
// Call this service's service handler. As a final safety measure,
// catch any exceptions (there should be none). We must be sure that we don't
// kill this thread, since it's shared by everyone in services.exe.
__try
{
dwRet = pThis->_pServiceHandler->ServiceHandler(dwControl, dwEventType, EventData, pData);
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
break;
case SERVICE_CONTROL_PAUSE:
pThis->SetServiceStatus(SERVICE_PAUSED, pThis->_dwControlsAccepted, NO_ERROR);
break;
case SERVICE_CONTROL_CONTINUE:
pThis->SetServiceStatus(SERVICE_RUNNING, pThis->_dwControlsAccepted, NO_ERROR);
break;
case SERVICE_CONTROL_INTERROGATE:
pThis->SetServiceStatus(pThis->_dwState, pThis->_dwControlsAccepted, NO_ERROR);
break;
case SERVICE_CONTROL_DEVICEEVENT:
break;
}
}
__except( BREAK_THEN_RETURN( EXCEPTION_EXECUTE_HANDLER ))
{
TrkLog(( TRKDBG_ERROR,
TEXT("Unexpected exception in CSvcCtrlInterface::ServiceHandler (%08x)"),
GetExceptionCode() ));
dwRet = ERROR_EXCEPTION_IN_SERVICE;
}
return dwRet;
}
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::SetServiceStatus
//
// Send a SetServiceStatus to the SCM. The checkpoint is automatically
// maintained by this class.
//
//+----------------------------------------------------------------------------
void
CSvcCtrlInterface::SetServiceStatus(DWORD dwState, DWORD dwControlsAccepted, DWORD dwWin32ExitCode)
{
SERVICE_STATUS ss;
_dwState = dwState;
_dwControlsAccepted = dwControlsAccepted;
if( SERVICE_START_PENDING != dwState
&&
SERVICE_STOP_PENDING != dwState )
{
_dwCheckPoint = 0;
}
ss.dwServiceType = SERVICE_WIN32; // XX_SC
ss.dwCurrentState = _dwState;
ss.dwControlsAccepted = _dwControlsAccepted;
ss.dwWin32ExitCode = dwWin32ExitCode;
ss.dwServiceSpecificExitCode = 0;
ss.dwCheckPoint = _dwCheckPoint++;
ss.dwWaitHint = DEFAULT_WAIT_HINT;
if (_ssh != 0)
{
if( !::SetServiceStatus(_ssh, &ss) )
{
TrkLog(( TRKDBG_ERROR, TEXT("SetServiceStatus(%s) failed, gle=%lu"),
(const TCHAR*)CDebugString(SServiceState(dwState)), GetLastError() ));
}
else
{
TrkLog(( TRKDBG_MISC, TEXT("SetServiceStatus(%s)"),
(const TCHAR*)CDebugString(SServiceState(dwState)) ));
}
}
}
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::UpdateWaitHint
//
// Send a non-default wait hint to the SCM.
//
//+----------------------------------------------------------------------------
void
CSvcCtrlInterface::UpdateWaitHint(DWORD dwMilliseconds)
{
SERVICE_STATUS ss;
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = _dwState;
ss.dwControlsAccepted = _dwControlsAccepted;
ss.dwWin32ExitCode = NO_ERROR;
ss.dwServiceSpecificExitCode = 0;
ss.dwCheckPoint = _dwCheckPoint++;
ss.dwWaitHint = dwMilliseconds;
if (_ssh != 0)
::SetServiceStatus(_ssh, &ss);
}
//+----------------------------------------------------------------------------
//
// StringizeServiceControl (debug only)
//
//+----------------------------------------------------------------------------
#if DBG
TCHAR * StringizeServiceControl( DWORD dwControl )
{
switch( dwControl )
{
case SERVICE_CONTROL_STOP:
return TEXT("SERVICE_CONTROL_STOP");
case SERVICE_CONTROL_PAUSE:
return TEXT("SERVICE_CONTROL_PAUSE");
case SERVICE_CONTROL_CONTINUE:
return TEXT("SERVICE_CONTROL_CONTINUE");
case SERVICE_CONTROL_INTERROGATE:
return TEXT("SERVICE_CONTROL_INTERROGATE");
case SERVICE_CONTROL_SHUTDOWN:
return TEXT("SERVICE_CONTROL_SHUTDOWN");
case SERVICE_CONTROL_PARAMCHANGE:
return TEXT("SERVICE_CONTROL_PARAMCHANGE");
case SERVICE_CONTROL_NETBINDADD:
return TEXT("SERVICE_CONTROL_NETBINDADD");
case SERVICE_CONTROL_NETBINDREMOVE:
return TEXT("SERVICE_CONTROL_NETBINDREMOVE");
case SERVICE_CONTROL_NETBINDENABLE:
return TEXT("SERVICE_CONTROL_NETBINDENABLE");
case SERVICE_CONTROL_NETBINDDISABLE:
return TEXT("SERVICE_CONTROL_NETBINDDISABLE");
case SERVICE_CONTROL_DEVICEEVENT:
return TEXT("SERVICE_CONTROL_DEVICEEVENT");
default:
return TEXT("Unknown");
}
}
#endif