#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 #include #include #include #include #include #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; }