// -------------------------------------------------------------------------- // 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 #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: // // 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: // // Returns: // // 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: // // 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: // // Returns: // // 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(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(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: // // Purpose: Constructor for CServiceWorkItem. // // History: 2000-11-29 vtan created // -------------------------------------------------------------------------- CServiceWorkItem::CServiceWorkItem (CServerAPI *pServerAPI) : _pServerAPI(pServerAPI) { pServerAPI->AddRef(); } // -------------------------------------------------------------------------- // CServiceWorkItem::~CServiceWorkItem // // Arguments: // // Returns: // // Purpose: Destructor for CServiceWorkItem. Release resources used. // // History: 2000-11-29 vtan created // -------------------------------------------------------------------------- CServiceWorkItem::~CServiceWorkItem (void) { _pServerAPI->Release(); _pServerAPI = NULL; } // -------------------------------------------------------------------------- // CServiceWorkItem::Entry // // Arguments: // // Returns: // // Purpose: Executes work item request (stop the server). // // History: 2000-11-29 vtan created // -------------------------------------------------------------------------- void CServiceWorkItem::Entry (void) { TSTATUS(_pServerAPI->Stop()); }