windows-nt/Source/XPSP1/NT/net/mmc/mprsnap/cservice.cpp
2020-09-26 16:20:57 +08:00

621 lines
16 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997 - 1998
//
// File: C S E R V I C E . C P P
//
// Contents: Implementation of non-inline CService and CServiceManager
// methods.
//
// Notes:
//
// Author: mikemi 6 Mar 1997
//
//----------------------------------------------------------------------------
#include "stdafx.h"
#include "cservice.h"
size_t CchMsz(const TCHAR * msz)
{
TCHAR * pch= (TCHAR *) msz;
while (*pch)
{
pch += lstrlen(pch)+1;
}
return (size_t) (pch-msz+1);
}
BOOL FIsSzInMultiSzSafe(LPCTSTR sz, LPCTSTR szMultiSz)
{
ULONG ulLen;
if (!szMultiSz || !sz)
return FALSE;
while (*szMultiSz)
{
ulLen = lstrlen(szMultiSz);
if (lstrcmpi(szMultiSz, sz)==0)
return TRUE;
szMultiSz += (ulLen + 1);
}
return FALSE;
}
HRESULT HrAddSzToMultiSz(LPCTSTR sz,
LPCTSTR mszIn,
LPTSTR * pmszOut)
{
HRESULT hr = S_OK;
Assert(pmszOut);
if (!FIsSzInMultiSzSafe(sz, mszIn)) // We need to add the string
{
size_t cchMszIn = CchMsz(mszIn);
size_t cchMszOut = cchMszIn + lstrlen(sz) + 1;
TCHAR * mszOut = new TCHAR[(int)cchMszOut];
ZeroMemory(mszOut, cchMszOut * sizeof(TCHAR));
// Copy the existing string
CopyMemory(mszOut, mszIn, (cchMszIn-1) * sizeof(TCHAR) );
// Add the new string
TCHAR * pchOut = mszOut;
pchOut += cchMszIn -1;
lstrcpy(pchOut, sz);
// Add the last '\0' for the output multisz
pchOut += lstrlen(sz) + 1;
*pchOut = '\0';
*pmszOut = mszOut;
}
else // We just make a copy of the input string
{
size_t cchMszOut = CchMsz(mszIn);
TCHAR * mszOut = new TCHAR[(int)cchMszOut];
// Copy the existing string
CopyMemory(mszOut, mszIn, cchMszOut*sizeof(TCHAR) );
*pmszOut = mszOut;
}
Trace1("HrAddSzToMultiSz %08lx", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrRemoveSzFromMultiSz
//
// Purpose: Remove a NULL terminated sz to a double NULL terminated Multi_Sz
//
// Arguments:
// sz [in] The string to remove
// mszIn [in] The Multi_Sz to remove from
// mszOut [out] The result Multi_Sz
//
// Returns: Always return S_OK for now
// Only possible failure is out of memory, which will throw exception
//
// Author: tongl 17 June 1997
//
// Notes: 1) This function only removes the first occurrance of the sz
// 2) The result multi_sz should be released using delete
//
HRESULT HrRemoveSzFromMultiSz(LPCTSTR sz,
LPCTSTR mszIn,
LPTSTR * pmszOut)
{
HRESULT hr = S_OK;
Assert(pmszOut);
if(FIsSzInMultiSzSafe(sz, mszIn)) // We need to remove the string
{
size_t cchIn = CchMsz(mszIn);
size_t cchOut = cchIn - lstrlen(sz)-1; // we assume the can string only appeared once
// Construct the output multi-sz
TCHAR * mszOut = new TCHAR[(int)cchOut];
ZeroMemory(mszOut, cchOut*sizeof(TCHAR));
TCHAR * pchIn = (TCHAR*) mszIn;
TCHAR * pchOut = mszOut;
while(*pchIn) // for each substring in mszIn
{
if(lstrcmpi(pchIn, sz) != 0) // if not the same as the string we are removing
{
lstrcpy(pchOut, pchIn);
pchIn += lstrlen(pchIn) + 1;
pchOut += lstrlen(pchOut) + 1;
}
else // skip the string we are deleting
{
pchIn += lstrlen(pchIn) + 1;
}
}
// Add the last '\0' of the multi-sz
*pchOut = '\0';
*pmszOut = mszOut;
}
else // We simply make a copy of the input string
{
size_t cchMszOut = CchMsz(mszIn);
TCHAR * mszOut = new TCHAR[(int)cchMszOut];
// Copy the existing string
CopyMemory(mszOut, mszIn, cchMszOut*sizeof(TCHAR));
*pmszOut = mszOut;
}
Trace1("HrRemoveSzFromMultiSz %08lx", hr);
return hr;
}
//-------------------------------------------------------------------
HRESULT CService::HrMoveOutOfState(DWORD dwState)
{
HRESULT hr = S_OK;
SERVICE_STATUS sStatus;
// Give the service a maximum of 30 seconds to start
UINT cTimeout = 30;
AssertSz((NULL != _schandle), "We don't have a service handle");
do
{
DWORD dwWait = 0;
// Get the status of the service
if (!::QueryServiceStatus(_schandle, &sStatus))
{
hr = HRESULT_FROM_WIN32(GetLastError());
break;
}
// We are not longer in the state we were waiting for
if (sStatus.dwCurrentState != dwState)
{
hr = S_OK;
break;
}
// Wait a second and or less for the service to start
dwWait = min((sStatus.dwWaitHint / 10), 1*(1000));
::Sleep(dwWait);
}
while(cTimeout--); // Make sure we don't get in an endless loop.
// Return an error if we timeout
if (0 == cTimeout)
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
AssertSz(FALSE,
"We timed out on waiting for a service. This is bad.");
}
Trace1("CService::HrMoveOutOfState", hr);
return hr;
}
//-------------------------------------------------------------------
HRESULT CService::HrQueryState( DWORD* pdwState )
{
SERVICE_STATUS sStatus;
Assert(_schandle != NULL );
Assert(pdwState != NULL );
if (!::QueryServiceStatus( _schandle, &sStatus ))
{
*pdwState = 0;
return HRESULT_FROM_WIN32(GetLastError());
}
*pdwState = sStatus.dwCurrentState;
return S_OK;
}
//-------------------------------------------------------------------
HRESULT CService::HrQueryStartType( DWORD* pdwStartType )
{
LPQUERY_SERVICE_CONFIG pqsConfig = NULL;
DWORD cbNeeded = sizeof( QUERY_SERVICE_CONFIG );
DWORD cbSize;
BOOL frt;
Assert(_schandle != NULL );
Assert(pdwStartType != NULL );
*pdwStartType = 0;
// loop, allocating the needed size
do
{
delete [] (PBYTE)pqsConfig;
pqsConfig = (LPQUERY_SERVICE_CONFIG) new BYTE[cbNeeded];
if (pqsConfig == NULL)
{
return E_OUTOFMEMORY;
}
cbSize = cbNeeded;
frt = ::QueryServiceConfig( _schandle,
pqsConfig,
cbSize,
&cbNeeded );
*pdwStartType = pqsConfig->dwStartType;
delete [] (PBYTE)pqsConfig;
pqsConfig = NULL;
if (!frt && (cbNeeded == cbSize))
{
// error
*pdwStartType = 0;
return HRESULT_FROM_WIN32(GetLastError());
}
} while (!frt && (cbNeeded != cbSize));
return S_OK;
}
//-------------------------------------------------------------------
HRESULT CService::HrQueryDependencies(OUT LPTSTR * pmszDependencyList)
{
HRESULT hr = S_OK;
LPQUERY_SERVICE_CONFIG pqsConfig = NULL;
DWORD cbNeeded = sizeof( QUERY_SERVICE_CONFIG );
DWORD cbSize;
BOOL frt;
Assert(_schandle != NULL );
Assert(pmszDependencyList);
// loop, allocating the needed size
do
{
delete [] (PBYTE)pqsConfig;
pqsConfig = (LPQUERY_SERVICE_CONFIG) new BYTE[cbNeeded];
if (pqsConfig == NULL)
{
hr = E_OUTOFMEMORY;
Trace1("CService::HrQueryDependencies", hr);
return hr;
}
cbSize = cbNeeded;
frt = ::QueryServiceConfig( _schandle,
pqsConfig,
cbSize,
&cbNeeded );
if (!frt && (cbNeeded == cbSize)) // error
{
delete [] (PBYTE)pqsConfig;
pqsConfig = NULL;
pmszDependencyList = NULL;
hr = HRESULT_FROM_WIN32(GetLastError());
Trace1("CService::HrQueryDependencies", hr);
return hr;
}
else if (frt && (cbNeeded != cbSize)) // We just need more space
{
delete [] (PBYTE)pqsConfig;
pqsConfig = NULL;
}
} while (!frt && (cbNeeded != cbSize));
// Copy pqsConfig->lpDependencies to *pmszDependencyList
// Allocating space
// int cch = CchMsz(pqsConfig->lpDependencies);
size_t cch=0;
TCHAR * pch= pqsConfig->lpDependencies;
while (*pch)
{
pch += lstrlen(pch)+1;
}
cch = (size_t)(pch - pqsConfig->lpDependencies +1);
TCHAR * mszOut;
mszOut = new TCHAR[(int)cch];
if (mszOut == NULL)
{
hr = E_OUTOFMEMORY;
Trace1("CService::HrQueryDependencies", hr);
return hr;
}
else
{
ZeroMemory(mszOut, cch * sizeof(TCHAR));
// Copy dependency list to mszOut
*pmszDependencyList = mszOut;
pch = pqsConfig->lpDependencies;
while (*pch)
{
lstrcpy(mszOut, pch);
mszOut += lstrlen(pch)+1;
pch += lstrlen(pch)+1;
}
mszOut = '\0';
}
delete [] (PBYTE)pqsConfig;
Trace1("CService::HrQueryDependencies", hr);
return hr;
}
//-------------------------------------------------------------------
HRESULT CServiceManager::HrQueryLocked(BOOL *pfLocked)
{
LPQUERY_SERVICE_LOCK_STATUS pqslStatus = NULL;
DWORD cbNeeded = sizeof( QUERY_SERVICE_LOCK_STATUS );
DWORD cbSize;
BOOL frt;
Assert(_schandle != NULL );
Assert(pfLocked != NULL);
*pfLocked = FALSE;
// loop, allocating the needed size
do
{
pqslStatus = (LPQUERY_SERVICE_LOCK_STATUS) new BYTE[cbNeeded];
if (pqslStatus == NULL)
{
return E_OUTOFMEMORY;
}
cbSize = cbNeeded;
frt = ::QueryServiceLockStatus( _schandle,
pqslStatus,
cbSize,
&cbNeeded );
*pfLocked = pqslStatus->fIsLocked;
delete [] (PBYTE)pqslStatus;
pqslStatus = NULL;
if (!frt && (cbNeeded == cbSize))
{
// if an error, treat this as a lock
return HRESULT_FROM_WIN32(GetLastError());
}
} while (!frt && (cbNeeded != cbSize));
return S_OK;
}
//+---------------------------------------------------------------------------
//
// Member: CServiceManager::HrStartServiceHelper
//
// Purpose: Starts the given service
//
// Arguments:
// szService [in] Name of service to start.
// eCriteria [in] if SERVICE_ONLY_AUTO_START, the service is only
// started if it is configured as auto-start
//
// Returns: S_OK if success, Win32 HRESULT otherwise.
//
// Author: danielwe 13 Jun 1997
//
// Notes:
//
HRESULT
CServiceManager::HrStartServiceHelper(LPCTSTR szService,
SERVICE_START_CRITERIA eCriteria)
{
HRESULT hr = S_OK;
CService service;
hr = HrOpenService(&service, szService);
if (SUCCEEDED(hr))
{
BOOL fStart = TRUE;
if (SERVICE_ONLY_AUTO_START == eCriteria)
{
DWORD dwStartType;
// only start services that are not disabled and not manual
hr = service.HrQueryStartType(&dwStartType);
if (FAILED(hr) ||
(SERVICE_DEMAND_START == dwStartType) ||
(SERVICE_DISABLED == dwStartType))
{
fStart = FALSE;
}
}
// If everything is okay to start then start it!
if (fStart)
{
hr = service.HrStart();
if (SUCCEEDED(hr))
{
// Make sure the service has started.
hr = service.HrMoveOutOfState(SERVICE_START_PENDING);
// Normalize result
if (SUCCEEDED(hr))
{
hr = S_OK;
}
}
else if (HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING) == hr)
{
// Ignore error if service is already running
hr = S_OK;
}
}
service.Close();
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CServiceManager::HrStopService
//
// Purpose: Stops the given service.
//
// Arguments:
// szService [in] Name of service to stop.
//
// Returns: S_OK if success, Win32 HRESULT otherwise.
//
// Author: danielwe 17 Jun 1997
//
// Notes: If service is not running, this returns S_OK.
//
HRESULT CServiceManager::HrStopService(LPCTSTR szService)
{
HRESULT hr = S_OK;
CService service;
hr = HrOpenService(&service, szService);
if (SUCCEEDED(hr))
{
hr = service.HrControl(SERVICE_CONTROL_STOP);
if (HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE) == hr)
{
// ignore error if the service is not running
hr = S_OK;
}
service.Close();
}
Trace1("CServiceManager::HrStopService", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CServiceManager::HrAddRemoveServiceDependency
//
// Purpose: Add/remove dependency to a service
//
// Arguments:
// szService [in] Name of service
// szDependency [in] Dependency to add
// enumFlag [in] Indicates add or remove
//
// Returns: S_OK if success, Win32 HRESULT otherwise.
//
// Author: tongl 17 Jun 1997
//
// Notes: this function is not for adding/removing group dependency
//
HRESULT CServiceManager::HrAddRemoveServiceDependency(LPCTSTR szServiceName,
LPCTSTR szDependency,
DEPENDENCY_ADDREMOVE enumFlag)
{
HRESULT hr = S_OK;
Assert(szServiceName);
Assert(szDependency);
Assert((enumFlag == DEPENDENCY_ADD) || (enumFlag == DEPENDENCY_REMOVE));
// If either string is empty, do nothing
if ((lstrlen(szDependency)>0) && (lstrlen(szServiceName)>0))
{
hr = HrLock();
if (SUCCEEDED(hr))
{
LPCTSTR szSrv = szDependency;
CService svc;
// Check if the dependency service exists
hr = HrOpenService(&svc, szDependency);
if SUCCEEDED(hr)
{
svc.Close();
// Open the service we are changing dependency on
szSrv = szServiceName;
hr = HrOpenService(&svc, szServiceName);
if (SUCCEEDED(hr))
{
LPTSTR mszDependencies;
hr = svc.HrQueryDependencies(&mszDependencies);
if(SUCCEEDED(hr) && mszDependencies)
{
TCHAR * mszNewDependencies;
if (enumFlag == DEPENDENCY_ADD)
{
hr = HrAddSzToMultiSz(szDependency, mszDependencies,
&mszNewDependencies);
}
else if (enumFlag == DEPENDENCY_REMOVE)
{
hr = HrRemoveSzFromMultiSz(szDependency, mszDependencies,
&mszNewDependencies);
}
if (SUCCEEDED(hr))
{
// Now set the new dependency
hr = svc.HrSetDependencies(const_cast<LPCTSTR>(mszNewDependencies));
delete [] mszNewDependencies;
}
}
delete [] mszDependencies;
svc.Close();
}
}
if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr) // If either services do not exist
{
#ifdef DEBUG
Trace1("CServiceManager::HrAddServiceDependency, Service %s does not exist.", szSrv);
#endif
hr = S_OK;
}
}
Unlock();
} // if szDependency is not empty string
Trace1("CServiceManager::HrAddServiceDependency", hr);
return hr;
}