|
|
#include "shsrvice.h"
#include "service.h"
#include "mischlpr.h"
#include "sfstr.h"
#include "str.h"
#include "reg.h"
#include "resource.h"
#include "dbg.h"
#include "tfids.h"
#include <dbt.h>
#include <initguid.h>
#include <ioevent.h>
#include <shlwapi.h>
#include <shlwapip.h>
#include <strsafe.h>
#define ARRAYSIZE(a) (sizeof((a))/sizeof((a)[0]))
struct CTRLEVENT { CTRLEVENT* peventNext; DWORD dwControl; DWORD dwEventType; BYTE rgbEventData[1]; };
const LPWSTR pszSVCHostGroup = TEXT("netsvcs");
///////////////////////////////////////////////////////////////////////////////
//
SERVICE_TABLE_ENTRY CGenericServiceManager::_rgste[] = { { TEXT("ShellHWDetection"), CGenericServiceManager::_ServiceMain }, { NULL, NULL }, };
CGenericServiceManager::SUBSERVICE CGenericServiceManager::_rgsubservice[] = { { TEXT("Shell.HWEventDetector"), IDS_SHELLHWDETECTION_FRIENDLYNAME, TEXT("RpcSs\0"), TEXT("ShellSvcGroup"), IDS_SHELLHWDETECTION_DESCRIPTION, {0} }, };
// "- 1": Last entry of both arrays are NULL terminators
DWORD CGenericServiceManager::_cste = ARRAYSIZE(CGenericServiceManager::_rgste) - 1;
CRITICAL_SECTION CGenericServiceManager::_csQueue = {0};
BOOL CGenericServiceManager::_fCritSectInit = FALSE; HANDLE CGenericServiceManager::_hEventInitCS = NULL;
#ifdef DEBUG
BOOL CGenericServiceManager::_fRunAsService = TRUE; #endif
///////////////////////////////////////////////////////////////////////////////
//
// static
BOOL _IsAlreadyInstalled(LPCWSTR pszRegStr, LPCWSTR pszServiceName) { LPCWSTR psz = pszRegStr; BOOL fThere = FALSE;
do { if (!lstrcmp(psz, pszServiceName)) { fThere = TRUE; break; }
psz += lstrlen(psz) + 1; } while (*psz);
return fThere; }
HRESULT _UnInstall(LPCWSTR pszServiceName) { HRESULT hres = E_FAIL; SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
// Need to do it for all services
if (hSCM) { hres = S_OK;
SC_HANDLE hService = OpenService(hSCM, pszServiceName, DELETE);
if (hService) { DeleteService(hService); CloseServiceHandle(hService); } else { // Don't fail, if somebody manually removed the service n
// from the reg, then all n + x services will not uninstall.
hres = S_FALSE; }
CloseServiceHandle(hSCM); } return hres; }
HRESULT _InstSetSVCHostInfo(LPWSTR pszServiceName) { HRESULT hres = E_FAIL; HKEY hkey; DWORD dwDisp; BOOL fAlreadyThere = FALSE;
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost"), 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hkey, &dwDisp)) { DWORD cbSize; DWORD cbSizeNew; BOOL fEmpty = FALSE; LPWSTR pszNew;
if (ERROR_SUCCESS != RegQueryValueEx(hkey, pszSVCHostGroup, 0, NULL, NULL, &cbSize)) { fEmpty = TRUE; // Set cbSize to the size of the 2nd NULL terminator
cbSize = sizeof(WCHAR); }
cbSizeNew = cbSize + (lstrlen(pszServiceName) + 1) * sizeof(WCHAR); pszNew = (LPWSTR)LocalAlloc(LPTR, cbSizeNew);
if (pszNew) { DWORD cbSize2 = cbSizeNew;
hres = S_OK;
if (!fEmpty) { if (ERROR_SUCCESS == RegQueryValueEx(hkey, pszSVCHostGroup, 0, NULL, (PBYTE)pszNew, &cbSize2)) { if (cbSize2 == cbSize) { fAlreadyThere = _IsAlreadyInstalled(pszNew, pszServiceName); } else { hres = E_FAIL; } } else { hres = E_FAIL; } } else { cbSize2 = sizeof(WCHAR); }
if (SUCCEEDED(hres) && !fAlreadyThere) { // We just allocated the buffer for this, it should not fail.
SHOULDNOTFAIL(SUCCEEDED(StringCchCopy(pszNew + (cbSize2 / sizeof(WCHAR)) - 1, (cbSizeNew / sizeof(WCHAR)) - (cbSize2 / sizeof(WCHAR)) + 1, pszServiceName)));
if (ERROR_SUCCESS != RegSetValueEx(hkey, pszSVCHostGroup, 0, REG_MULTI_SZ, (PBYTE)pszNew, cbSizeNew)) { hres = E_FAIL; } }
LocalFree((HLOCAL)pszNew); } else { hres = E_OUTOFMEMORY; }
// We should have an entry in the SUBSERVICE array for this...
if (SUCCEEDED(hres)) { HKEY hkey2;
if (ERROR_SUCCESS == RegCreateKeyEx(hkey, pszSVCHostGroup, 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hkey2, &dwDisp)) { DWORD dwSec = 0x00000001; DWORD cbSec = sizeof(dwSec);
// We set this magic value of 1 and svchost.exe will
// call CoInitializeSecurity for us
if (ERROR_SUCCESS != RegSetValueEx(hkey2, TEXT("CoInitializeSecurityParam"), 0, REG_DWORD, (PBYTE)&dwSec, cbSec)) { hres = E_FAIL; } }
RegCloseKey(hkey2); }
RegCloseKey(hkey); }
return hres; }
HRESULT _InstSetParameters(LPWSTR pszServiceName) { HKEY hkeyServices; HRESULT hres = E_FAIL;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\"), 0, MAXIMUM_ALLOWED, &hkeyServices)) { HKEY hkeySvc;
if (ERROR_SUCCESS == RegOpenKeyEx(hkeyServices, pszServiceName, 0, MAXIMUM_ALLOWED, &hkeySvc)) { HKEY hkeyParam;
if (ERROR_SUCCESS == RegCreateKeyEx(hkeySvc, TEXT("Parameters"), 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hkeyParam, NULL)) { // Watch out! Hard coded path and filename!
WCHAR szServiceDll[] = TEXT("%SystemRoot%\\System32\\shsvcs.dll");
if (ERROR_SUCCESS == RegSetValueEx(hkeyParam, TEXT("ServiceDll"), 0, REG_EXPAND_SZ, (PBYTE)szServiceDll, sizeof(szServiceDll))) { WCHAR szServiceMain[] = TEXT("HardwareDetectionServiceMain");
if (ERROR_SUCCESS == RegSetValueEx( hkeyParam, TEXT("ServiceMain"), 0, REG_SZ, (PBYTE)szServiceMain, sizeof(szServiceMain))) { hres = S_OK; } }
RegCloseKey(hkeyParam); }
RegCloseKey(hkeySvc); }
RegCloseKey(hkeyServices); }
return hres; }
// static
HRESULT CGenericServiceManager::UnInstall() { HRESULT hr = S_FALSE;
for (DWORD dw = 0; SUCCEEDED(hr) && (dw < _cste); ++dw) { hr = _UnInstall(_rgste[dw].lpServiceName); }
return hr; }
HRESULT _GetFriendlyStrings(CGenericServiceManager::SUBSERVICE* psubservice, LPWSTR pszFriendlyName, DWORD cchFriendlyName, LPWSTR pszDescription, DWORD cchDescription) { *pszFriendlyName = 0; *pszDescription = 0;
HMODULE hmodule = GetModuleHandle(TEXT("shsvcs.dll"));
if (hmodule) { LoadString(hmodule, psubservice->uFriendlyName, pszFriendlyName, cchFriendlyName);
LoadString(hmodule, psubservice->uDescription, pszDescription, cchDescription); }
return S_OK; }
// static
HRESULT CGenericServiceManager::Install() { HRESULT hres = S_FALSE; if (!IsOS(OS_WOW6432)) { hres = E_FAIL; SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); DWORD dwStartType = SERVICE_AUTO_START;
if (hSCM) { WCHAR szFriendlyName[200]; // Doc says limit is 1024 bytes
WCHAR szDescription[1024 / sizeof(WCHAR)];
// Need to do it for all services
hres = S_OK;
for (DWORD dw = 0; SUCCEEDED(hres) && (dw < _cste); ++dw) { WCHAR szCmd[MAX_PATH] = TEXT("%SystemRoot%\\System32\\svchost.exe -k ");
hres = SafeStrCatN(szCmd, pszSVCHostGroup, ARRAYSIZE(szCmd));
if (SUCCEEDED(hres)) { _GetFriendlyStrings(&(_rgsubservice[dw]), szFriendlyName, ARRAYSIZE(szFriendlyName), szDescription, ARRAYSIZE(szDescription));
SC_HANDLE hService = CreateService(hSCM, _rgste[dw].lpServiceName, szFriendlyName, SERVICE_CHANGE_CONFIG, SERVICE_WIN32_SHARE_PROCESS, dwStartType, SERVICE_ERROR_IGNORE, szCmd, _rgsubservice[dw].pszLoadOrderGroup, NULL, _rgsubservice[dw].pszDependencies, NULL, NULL);
if (hService) { SERVICE_DESCRIPTION sd;
sd.lpDescription = szDescription;
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sd);
CloseServiceHandle(hService);
hres = _InstSetParameters(_rgste[dw].lpServiceName);
if (SUCCEEDED(hres)) { hres = _InstSetSVCHostInfo( _rgste[dw].lpServiceName); } } else { if (ERROR_SERVICE_EXISTS == GetLastError()) { // We had this problem on upgrade. The service is
// already there, so CreateService fails. As a result the
// StartType was not switched from Demand Start to Auto Start.
// The following lines will do just that.
// This code should be expanded for general upgraded cases.
// All the other values in the structure should be passed here.
hService = OpenService(hSCM, _rgste[dw].lpServiceName, SERVICE_CHANGE_CONFIG);
if (hService) { if (ChangeServiceConfig( hService, // handle to service
SERVICE_NO_CHANGE, // type of service
dwStartType, // when to start service
SERVICE_NO_CHANGE, // severity of start failure
szCmd, // service binary file name
_rgsubservice[dw].pszLoadOrderGroup, // load ordering group name
NULL, // tag identifier
NULL, // array of dependency names
NULL, // account name
NULL, // account password
NULL // display name
)) { // Do this un upgrade too
hres = _InstSetSVCHostInfo( _rgste[dw].lpServiceName);
if (SUCCEEDED(hres)) { hres = _InstSetParameters(_rgste[dw].lpServiceName); } } else { hres = E_FAIL; }
SERVICE_DESCRIPTION sd;
sd.lpDescription = szDescription;
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sd);
CloseServiceHandle(hService); }
} else { hres = E_FAIL; } } } }
CloseServiceHandle(hSCM); } // We don't need the ShellCOMServer anymore, so let's nuke it away on upgrades
_UnInstall(TEXT("ShellCOMServer")); // Also remove the following reg entry on upgrade
_RegDeleteValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\SvcHost"), TEXT("shsvc")); }
return hres; }
// static
HRESULT CGenericServiceManager::DllAttach(HINSTANCE UNREF_PARAM(hinst)) { HRESULT hr;
if (InitializeCriticalSectionAndSpinCount(&_csQueue, 0)) { _fCritSectInit = TRUE; hr = S_OK; } else { hr = E_OUTOFMEMORY; } return hr; }
// static
HRESULT CGenericServiceManager::DllDetach() { if (_fCritSectInit) { DeleteCriticalSection(&_csQueue); _fCritSectInit = FALSE; }
return S_OK; }
///////////////////////////////////////////////////////////////////////////////
// Private
// static
HRESULT CGenericServiceManager::_Init() { ASSERT(ARRAYSIZE(_rgste) == (ARRAYSIZE(_rgsubservice) + 1));
// Per thread
return CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); }
// static
HRESULT CGenericServiceManager::_Cleanup() { // Per thread
CoUninitialize();
return S_OK; }
__inline void _TraceServiceCode(DWORD #ifdef DEBUG
dwControl #endif
) { #ifdef DEBUG
LPWSTR pszControl = TEXT("Unknown");
switch (dwControl) { case SERVICE_CONTROL_STOP: pszControl = TEXT("SERVICE_CONTROL_STOP"); break; case SERVICE_CONTROL_PAUSE: pszControl = TEXT("SERVICE_CONTROL_PAUSE"); break; case SERVICE_CONTROL_CONTINUE: pszControl = TEXT("SERVICE_CONTROL_CONTINUE"); break; case SERVICE_CONTROL_INTERROGATE: pszControl = TEXT("SERVICE_CONTROL_INTERROGATE"); break; case SERVICE_CONTROL_SHUTDOWN: pszControl = TEXT("SERVICE_CONTROL_SHUTDOWN"); break; case SERVICE_CONTROL_PARAMCHANGE: pszControl = TEXT("SERVICE_CONTROL_PARAMCHANGE"); break; case SERVICE_CONTROL_NETBINDADD: pszControl = TEXT("SERVICE_CONTROL_NETBINDADD"); break; case SERVICE_CONTROL_NETBINDREMOVE: pszControl = TEXT("SERVICE_CONTROL_NETBINDREMOVE"); break; case SERVICE_CONTROL_NETBINDENABLE: pszControl = TEXT("SERVICE_CONTROL_NETBINDENABLE"); break; case SERVICE_CONTROL_NETBINDDISABLE: pszControl = TEXT("SERVICE_CONTROL_NETBINDDISABLE"); break; case SERVICE_CONTROL_DEVICEEVENT: pszControl = TEXT("SERVICE_CONTROL_DEVICEEVENT"); break; case SERVICE_CONTROL_HARDWAREPROFILECHANGE: pszControl = TEXT("SERVICE_CONTROL_HARDWAREPROFILECHANGE"); break; case SERVICE_CONTROL_POWEREVENT: pszControl = TEXT("SERVICE_CONTROL_POWEREVENT"); break; case SERVICE_CONTROL_SESSIONCHANGE: pszControl = TEXT("SERVICE_CONTROL_SESSIONCHANGE"); break; }
TRACE(TF_SERVICE, TEXT("Received Service Control code: %s (0x%08X)"), pszControl, dwControl); #endif
}
///////////////////////////////////////////////////////////////////////////////
// Private
//static
DWORD WINAPI CGenericServiceManager::_ServiceHandler(DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, LPVOID lpContext) { // We don't want to deny any request, so return NO_ERROR whatever happens
DWORD dwRet = NO_ERROR;
// This is called on the main thread not the thread of the specific
// service, so keep it short and sweet
SERVICEENTRY* pse = (SERVICEENTRY*)lpContext;
if (pse) { BOOL fProcess = FALSE; BOOL fSynch = FALSE; HRESULT hres = S_OK;
switch (dwControl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: TRACE(TF_SERVICE, TEXT("Received SERVICE_CONTROL_SHUTDOWN or STOP, will skip all other terminating events"));
if (!pse->_fSkipTerminatingEvents) { fProcess = TRUE; pse->_fSkipTerminatingEvents = TRUE; } else { TRACE(TF_SERVICE, TEXT("Skipping terminating event")); }
break;
case SERVICE_CONTROL_INTERROGATE: // Special case SERVICE_CONTROL_INTERROGATE. We don't really need the
// IService impl to process this. The service will also be more
// responsive this way. The state can be queried in the middle of
// execution.
TRACE(TF_SERVICE, TEXT("Received SERVICE_CONTROL_INTERROGATE"));
_SetServiceStatus(pse); break;
default: if (!pse->_fSkipTerminatingEvents) { fProcess = TRUE;
hres = _EventNeedsToBeProcessedSynchronously(dwControl, dwEventType, pvEventData, pse, &fSynch); } break; }
if (SUCCEEDED(hres) && fProcess) { _TraceServiceCode(dwControl);
EnterCriticalSection(&_csQueue);
hres = _QueueEvent(pse, dwControl, dwEventType, pvEventData);
if (SUCCEEDED(hres)) { // Let the service process events
SetEvent(pse->_hEventRelinquishControl);
ResetEvent(pse->_hEventSynchProcessing); }
LeaveCriticalSection(&_csQueue);
if (SUCCEEDED(hres) && fSynch) { Sleep(0);
TRACE(TF_SERVICE, TEXT("=========== Processing SYNCHRONOUSLY ==========="));
// We have to wait before we return... (at most 20 sec)
DWORD dwWait = WaitForSingleObject(pse->_hEventSynchProcessing, 20000);
if (WAIT_TIMEOUT == dwWait) { TRACE(TF_SERVICE, TEXT("=========== WAIT TIMED OUT ===========")); }
TRACE(TF_SERVICE, TEXT("=========== FINISHED processing SYNCHRONOUSLY ==========="));
#ifdef DEBUG
// If we get the notifs from a windowproc, return TRUE,
// or else we'll deny any request to remove, lock, ...
if (!_fRunAsService) { if (SERVICE_CONTROL_DEVICEEVENT == dwControl) { dwRet = TRUE; } } #endif
} } }
return dwRet; }
//static
void WINAPI CGenericServiceManager::_ServiceMain(DWORD cArg, LPWSTR* ppszArgs) { SERVICEENTRY* pse; LPCWSTR pszServiceName = *ppszArgs;
HRESULT hres = _Init();
if (SUCCEEDED(hres)) { hres = _InitServiceEntry(pszServiceName, &pse);
if (SUCCEEDED(hres)) { hres = _RegisterServiceCtrlHandler(pszServiceName, pse);
if (SUCCEEDED(hres)) { pse->_servicestatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; pse->_servicestatus.dwCurrentState = SERVICE_START_PENDING;
_SetServiceStatus(pse);
hres = _CreateIService(pszServiceName, &(pse->_pservice));
if (SUCCEEDED(hres)) { hres = pse->_pservice->InitMinimum(cArg, ppszArgs, pse->_szServiceEventName, &(pse->_servicestatus.dwControlsAccepted), &(pse->_fWantsDeviceEvents));
if (SUCCEEDED(hres)) { hres = _HandleWantsDeviceEvents(pszServiceName, pse->_fWantsDeviceEvents);
if (SUCCEEDED(hres)) { if (pse->_fWantsDeviceEvents) { hres = pse->_pservice->InitDeviceEventHandler( pse->_ssh); }
if (SUCCEEDED(hres)) { pse->_servicestatus.dwCurrentState = SERVICE_RUNNING;
_SetServiceStatus(pse);
hres = pse->_pservice->InitFinal();
if (SUCCEEDED(hres)) { do { hres = pse->_pservice->Run();
if (SUCCEEDED(hres)) { // The service has finished its business or it
// relinquished control because
// _hEventRelinquishControl is set
//
// If it relinquished control because of a
// service control event, then let's process it
DWORD dwWait = WaitForSingleObject( pse->_hEventRelinquishControl, INFINITE);
TRACE(TF_SERVICEDETAILED, TEXT("WaitForSingleObj returned with: 0x%08X"), dwWait);
if (WAIT_OBJECT_0 == dwWait) { // Process all Service Control codes
// received.
hres = _ProcessServiceControlCodes( pse); } else { hres = E_FAIL; } } } while (SUCCEEDED(hres) && (SERVICE_STOPPED != pse->_servicestatus.dwCurrentState)); } } } // What do we do with hres?
} } } else { #ifdef DEBUG
TRACE(TF_SERVICEDETAILED, TEXT("%s: _RegisterServiceCtrlHandler FAILED: 0x%08X"), pse->_szServiceName, hres); #endif
}
_CleanupServiceEntry(pse); }
if (SUCCEEDED(hres) && (SERVICE_STOPPED == pse->_servicestatus.dwCurrentState)) { _SetServiceStatus(pse); }
_Cleanup(); }
TRACE(TF_SERVICE, TEXT("Exiting _ServiceMain for Service: %s"), pszServiceName); }
//static
HRESULT CGenericServiceManager::_ProcessServiceControlCodes(SERVICEENTRY* pse) { HRESULT hres; BOOL fEndLoop = FALSE;
TRACE(TF_SERVICEDETAILED, TEXT("Entered _ProcessServiceControlCodes"));
do { CTRLEVENT* pevent;
EnterCriticalSection(&_csQueue);
TRACE(TF_SERVICEDETAILED, TEXT("Entered _ProcessServiceControlCodes' Critical Section"));
hres = _DeQueueEvent(pse, &pevent);
TRACE(TF_SERVICE, TEXT("DeQueued Event: 0x%08X"), hres);
if (!pse->_peventQueueHead) { ASSERT(!pse->_cEvents);
fEndLoop = TRUE;
ResetEvent(pse->_hEventRelinquishControl); }
LeaveCriticalSection(&_csQueue);
if (SUCCEEDED(hres)) { ///////////////////////////////////////////////////////////////////
// If we're here then we should have received some service control,
// or the IService decided it had nothing more to process
//
TRACE(TF_SERVICEDETAILED, TEXT("Will call _HandleServiceControls (dwControl = 0x%08X)"), pevent->dwControl);
hres = _HandleServiceControls(pse, pevent->dwControl, pevent->dwEventType, (PVOID)(pevent->rgbEventData));
if (SERVICE_CONTROL_DEVICEEVENT == pevent->dwControl) { // Never ever return something else than NO_ERROR (S_OK) for these
hres = NO_ERROR; }
TRACE(TF_SERVICE, TEXT("_HandleServiceControls returned: 0x%08X"), hres);
LocalFree((HLOCAL)pevent); }
if (fEndLoop) { TRACE(TF_SERVICEDETAILED, TEXT("Resetting RelinquishEvent"));
SetEvent(pse->_hEventSynchProcessing); } } while (!fEndLoop && SUCCEEDED(hres));
TRACE(TF_SERVICEDETAILED, TEXT("Exiting _ProcessServiceControlCodes"));
return hres; }
#pragma warning(push)
// FALSE positive below: fPending
#pragma warning(disable : 4701)
//static
HRESULT CGenericServiceManager::_HandleServiceControls(SERVICEENTRY* pse, DWORD dwControl, DWORD dwEventType, PVOID pvEventData) { HRESULT hres = _HandlePreState(pse, dwControl);
TRACE(TF_SERVICEDETAILED, TEXT("_HandlePreState returned: 0x%08X, status: 0x%08X"), hres, pse->_servicestatus.dwCurrentState);
if (SUCCEEDED(hres)) { if (S_OK == hres) { switch (dwControl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_PAUSE: case SERVICE_CONTROL_CONTINUE: case SERVICE_CONTROL_SHUTDOWN: { BOOL fPending;
do { // This will return S_FALSE if it's pending
hres = pse->_pservice->HandleServiceControl(dwControl, &(pse->_servicestatus.dwWaitHint));
if (SUCCEEDED(hres)) { if (S_FALSE == hres) { ASSERT(pse->_servicestatus.dwWaitHint);
fPending = TRUE; } else { fPending = FALSE; }
TRACE(TF_SERVICE, TEXT("Will call _HandlePostState (fPending = %d)"), fPending);
hres = _HandlePostState(pse, dwControl, fPending);
TRACE(TF_SERVICE, TEXT("_HandlePostState returned: 0x%08X, status: 0x%08X"), hres, pse->_servicestatus.dwCurrentState); } } while (SUCCEEDED(hres) && fPending);
break; }
case SERVICE_CONTROL_DEVICEEVENT: TRACE(TF_SERVICE, TEXT("Received SERVICE_CONTROL_DEVICEEVENT"));
if (!(pse->_fSkipTerminatingEvents)) { hres = pse->_pservice->HandleDeviceEvent(dwEventType, pvEventData); } else { hres = S_OK; } break;
case SERVICE_CONTROL_SESSIONCHANGE: TRACE(TF_SERVICE, TEXT("Received: SERVICE_CONTROL_SESSIONCHANGE"));
if (!(pse->_fSkipTerminatingEvents)) { hres = pse->_pservice->HandleSessionChange(dwEventType, pvEventData); } else { hres = S_OK; }
break;
default: TRACE(TF_SERVICE, TEXT("Received unhandled service control"));
hres = S_FALSE; break; } } }
return hres; } #pragma warning(pop)
// static
HRESULT CGenericServiceManager::_GetServiceIndex(LPCWSTR pszServiceName, DWORD* pdw) { HRESULT hres = E_FAIL;
ASSERT(pszServiceName); ASSERT(pdw);
for (DWORD dw = 0; FAILED(hres) && (dw < _cste); ++dw) { if (!lstrcmp(pszServiceName, _rgste[dw].lpServiceName)) { // Found it
*pdw = dw;
hres = S_OK; } }
return hres; }
HRESULT CGenericServiceManager::_GetServiceCLSID(LPCWSTR pszServiceName, CLSID* pclsid) { ASSERT(pszServiceName); ASSERT(pclsid);
DWORD dw; HRESULT hres = _GetServiceIndex(pszServiceName, &dw);
if (SUCCEEDED(hres)) { // Found it
hres = CLSIDFromProgID(_rgsubservice[dw].pszProgID, pclsid); }
return hres; }
HRESULT CGenericServiceManager::_CreateIService(LPCWSTR pszServiceName, IService** ppservice) { CLSID clsid; HRESULT hres = _GetServiceCLSID(pszServiceName, &clsid);
*ppservice = NULL;
if (SUCCEEDED(hres)) { hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IService, ppservice)); }
return hres; }
HRESULT CGenericServiceManager::_InitServiceEntry(LPCWSTR pszServiceName, SERVICEENTRY** ppse) { DWORD dw; HRESULT hres = _GetServiceIndex(pszServiceName, &dw);
if (SUCCEEDED(hres)) { *ppse = &(_rgsubservice[dw].se);
ZeroMemory(*ppse, sizeof(**ppse));
// We use a GUID so that the name cannot be guessed and the event
// spoofed.
hres = _CreateGUID((*ppse)->_szServiceEventName, ARRAYSIZE((*ppse)->_szServiceEventName));
if (SUCCEEDED(hres)) { hres = SafeStrCatN((*ppse)->_szServiceEventName, pszServiceName, ARRAYSIZE((*ppse)->_szServiceEventName)); }
if (SUCCEEDED(hres)) { (*ppse)->_hEventRelinquishControl = CreateEvent(NULL, TRUE, FALSE, (*ppse)->_szServiceEventName);
if (!((*ppse)->_hEventRelinquishControl)) { hres = E_FAIL; }
if (SUCCEEDED(hres)) { (*ppse)->_hEventSynchProcessing = CreateEvent(NULL, TRUE, TRUE, NULL);
if (!((*ppse)->_hEventSynchProcessing)) { hres = E_FAIL; } }
if (FAILED(hres)) { _CleanupServiceEntry(*ppse); } } }
return hres; }
HRESULT CGenericServiceManager::_CleanupServiceEntry(SERVICEENTRY* pse) { if (pse->_pservice) { pse->_pservice->Release(); }
if (pse->_hEventRelinquishControl) { CloseHandle(pse->_hEventRelinquishControl); }
if (pse->_hEventSynchProcessing) { CloseHandle(pse->_hEventSynchProcessing); }
return S_OK; }
//static
HRESULT CGenericServiceManager::_HandlePreState(SERVICEENTRY* pse, DWORD dwControl) { HRESULT hres; BOOL fSetServiceStatus = TRUE;
// _HandleServiceControls will loop until we are not in a pending state.
// All incoming ctrl events are queued, and will be processed on this same
// thread, so we should never enter this fct in a pending state.
ASSERT(SERVICE_STOP_PENDING != pse->_servicestatus.dwCurrentState); ASSERT(SERVICE_START_PENDING != pse->_servicestatus.dwCurrentState); ASSERT(SERVICE_CONTINUE_PENDING != pse->_servicestatus.dwCurrentState); ASSERT(SERVICE_PAUSE_PENDING != pse->_servicestatus.dwCurrentState);
// Should have been processed in _ServiceHandler
ASSERT(SERVICE_CONTROL_INTERROGATE != dwControl);
// We cleanup a bit. If the request is incompatible with the current state
// then we return S_FALSE to instruct _HandleServiceControls to not call
// the IService impl for nothing.
switch (dwControl) { case SERVICE_CONTROL_STOP: switch (pse->_servicestatus.dwCurrentState) { case SERVICE_STOPPED: hres = S_FALSE; break;
case SERVICE_RUNNING: case SERVICE_PAUSED: default: pse->_servicestatus.dwCurrentState = SERVICE_STOP_PENDING; hres = S_OK; break; } break; case SERVICE_CONTROL_PAUSE: ASSERT(SERVICE_STOPPED != pse->_servicestatus.dwCurrentState);
switch (pse->_servicestatus.dwCurrentState) { case SERVICE_PAUSED: hres = S_FALSE; break;
case SERVICE_STOPPED: // Weird, think about it...
hres = S_FALSE; break;
case SERVICE_RUNNING: default: pse->_servicestatus.dwCurrentState = SERVICE_PAUSE_PENDING; hres = S_OK; break; } break; case SERVICE_CONTROL_CONTINUE: ASSERT(SERVICE_STOPPED != pse->_servicestatus.dwCurrentState);
switch (pse->_servicestatus.dwCurrentState) { case SERVICE_RUNNING: hres = S_FALSE; break;
case SERVICE_STOPPED: // Weird, think about it...
hres = S_FALSE; break;
case SERVICE_PAUSED: default: pse->_servicestatus.dwCurrentState = SERVICE_CONTINUE_PENDING; hres = S_OK; break; } break;
case SERVICE_CONTROL_SHUTDOWN:
fSetServiceStatus = FALSE;
hres = S_OK; break;
case SERVICE_CONTROL_DEVICEEVENT:
fSetServiceStatus = FALSE;
if (pse->_fWantsDeviceEvents) { hres = S_OK; } else { hres = S_FALSE; } break;
case SERVICE_CONTROL_SESSIONCHANGE:
fSetServiceStatus = FALSE;
hres = S_OK; break;
default: hres = S_FALSE; break; }
if (fSetServiceStatus) { _SetServiceStatus(pse); }
return hres; }
//static
HRESULT CGenericServiceManager::_HandlePostState(SERVICEENTRY* pse, DWORD dwControl, BOOL fPending) { HRESULT hres = S_FALSE;
// All incoming ctrl events are queued, and will be processed on this same
// thread, so if we are pending, the dwControl should be compatible with
// our current pending state. We call _SetServiceStatus to update the
// dwWaitHint.
// We should already be in a pending state. This should have been set
// by _HandlePreState. Just make sure of this.
// Should have been processed in _ServiceHandler
ASSERT(SERVICE_CONTROL_INTERROGATE != dwControl);
switch (dwControl) { case SERVICE_CONTROL_STOP: ASSERT(SERVICE_STOP_PENDING == pse->_servicestatus.dwCurrentState);
if (!fPending) { pse->_servicestatus.dwCurrentState = SERVICE_STOPPED; }
break;
case SERVICE_CONTROL_PAUSE: ASSERT(SERVICE_PAUSE_PENDING == pse->_servicestatus.dwCurrentState);
if (!fPending) { pse->_servicestatus.dwCurrentState = SERVICE_PAUSED; }
break;
case SERVICE_CONTROL_CONTINUE: ASSERT(SERVICE_CONTINUE_PENDING == pse->_servicestatus.dwCurrentState);
if (!fPending) { pse->_servicestatus.dwCurrentState = SERVICE_RUNNING; }
break;
case SERVICE_CONTROL_SHUTDOWN: ASSERT(!fPending);
pse->_servicestatus.dwCurrentState = SERVICE_STOPPED;
break; }
if (SERVICE_STOPPED != pse->_servicestatus.dwCurrentState) { _SetServiceStatus(pse); }
return hres; }
// static
HRESULT CGenericServiceManager::_EventNeedsToBeProcessedSynchronously( DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, SERVICEENTRY*, BOOL* pfBool) { *pfBool = FALSE;
if (SERVICE_CONTROL_DEVICEEVENT == dwControl) { if (pvEventData) { DEV_BROADCAST_HDR* dbhdr = (DEV_BROADCAST_HDR*)pvEventData;
if (DBT_DEVTYP_HANDLE == dbhdr->dbch_devicetype) { if (DBT_DEVICEQUERYREMOVE == dwEventType) { TRACE(TF_SERVICE, TEXT("Received DBT_DEVICEQUERYREMOVE"));
*pfBool = TRUE; } else { if (DBT_CUSTOMEVENT == dwEventType) { DEV_BROADCAST_HANDLE* pdbh = (DEV_BROADCAST_HANDLE*)dbhdr;
if ((GUID_IO_VOLUME_LOCK == pdbh->dbch_eventguid)) { TRACE(TF_SERVICE, TEXT("------------Received GUID_IO_VOLUME_LOCK------------")); }
if ((GUID_IO_VOLUME_LOCK_FAILED == pdbh->dbch_eventguid)) { TRACE(TF_SERVICE, TEXT("------------Received GUID_IO_VOLUME_LOCK_FAILED------------")); }
if ((GUID_IO_VOLUME_UNLOCK == pdbh->dbch_eventguid)) { TRACE(TF_SERVICE, TEXT("------------Received GUID_IO_VOLUME_UNLOCK------------")); }
if ((GUID_IO_VOLUME_LOCK == pdbh->dbch_eventguid) || (GUID_IO_VOLUME_LOCK_FAILED == pdbh->dbch_eventguid) || (GUID_IO_VOLUME_UNLOCK == pdbh->dbch_eventguid)) { *pfBool = TRUE; } } } } } }
return S_OK; }
// static
HRESULT CGenericServiceManager::_MakeEvent(DWORD dwControl, DWORD dwEventType, PVOID pvEventData, CTRLEVENT** ppevent) { HRESULT hres = S_OK;
DWORD cbSize = sizeof(CTRLEVENT); CTRLEVENT* pevent;
if (SERVICE_CONTROL_DEVICEEVENT == dwControl) { if (pvEventData) { cbSize += ((DEV_BROADCAST_HDR*)pvEventData)->dbch_size; } }
pevent = (CTRLEVENT*)LocalAlloc(LPTR, cbSize);
if (pevent) { // Payload
pevent->dwControl = dwControl; pevent->dwEventType = dwEventType;
*ppevent = pevent;
if (cbSize > sizeof(CTRLEVENT)) { if (pvEventData) { CopyMemory(pevent->rgbEventData, pvEventData, cbSize - sizeof(CTRLEVENT)); } } } else { hres = E_OUTOFMEMORY; }
return hres; }
// static
HRESULT CGenericServiceManager::_QueueEvent(SERVICEENTRY* pse, DWORD dwControl, DWORD dwEventType, PVOID pvEventData) { CTRLEVENT* pevent; HRESULT hres = _MakeEvent(dwControl, dwEventType, pvEventData, &pevent);
if (SUCCEEDED(hres)) { // We add at tail, remove at head
// Prev: closer to head
// Next: closer to tail
pevent->peventNext = NULL;
if (pse->_peventQueueTail) { ASSERT(!(pse->_peventQueueTail->peventNext)); pse->_peventQueueTail->peventNext = pevent; }
pse->_peventQueueTail = pevent;
if (!pse->_peventQueueHead) { pse->_peventQueueHead = pse->_peventQueueTail; }
#ifdef DEBUG
++(pse->_cEvents);
if (1 == pse->_cEvents) { ASSERT(pse->_peventQueueHead == pse->_peventQueueTail); } else { if (0 == pse->_cEvents) { ASSERT(!pse->_peventQueueHead && !pse->_peventQueueTail); } else { ASSERT(pse->_peventQueueHead && pse->_peventQueueTail && (pse->_peventQueueHead != pse->_peventQueueTail)); } } #endif
} else { hres = E_OUTOFMEMORY; }
return hres; }
// static
HRESULT CGenericServiceManager::_DeQueueEvent(SERVICEENTRY* pse, CTRLEVENT** ppevent) { ASSERT(pse->_peventQueueHead);
// We add at tail, remove at head
// Prev: closer to head
// Next: closer to tail
CTRLEVENT* peventRet = pse->_peventQueueHead; CTRLEVENT* peventNewHead = peventRet->peventNext;
// Any elem left after removing head?
if (!peventNewHead) { // No
pse->_peventQueueTail = NULL; }
pse->_peventQueueHead = peventNewHead;
peventRet->peventNext = NULL; *ppevent = peventRet;
#ifdef DEBUG
--(pse->_cEvents);
if (1 == pse->_cEvents) { ASSERT(pse->_peventQueueHead == pse->_peventQueueTail); } else { if (0 == pse->_cEvents) { ASSERT(!pse->_peventQueueHead && !pse->_peventQueueTail); } else { ASSERT(pse->_peventQueueHead && pse->_peventQueueTail && (pse->_peventQueueHead != pse->_peventQueueTail)); } } #endif
return S_OK; }
|