370 lines
13 KiB
C++
370 lines
13 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: E X P O R T S . C P P
|
|
//
|
|
// Contents: Exported functions from NETCFG.DLL
|
|
//
|
|
// Notes:
|
|
//
|
|
// Author: danielwe 5 Dec 1997
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
#include "ncreg.h"
|
|
#include "ncsetup.h"
|
|
#include "ncsvc.h"
|
|
|
|
#define REGSTR_PATH_SVCHOST L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost"
|
|
|
|
HRESULT
|
|
HrPrepareForSvchostEnum (
|
|
IN PCWSTR pszService,
|
|
IN OUT CServiceManager* pscm,
|
|
IN OUT CService* psvc,
|
|
OUT LPQUERY_SERVICE_CONFIG* ppOriginalConfig,
|
|
OUT HKEY* phkeySvchost,
|
|
OUT PWSTR* ppszValueNameBuffer,
|
|
OUT DWORD* pcchValueNameBuffer,
|
|
OUT PWSTR* ppmszValueBuffer,
|
|
OUT DWORD* pcbValueBuffer)
|
|
{
|
|
// Initialize the output parameters.
|
|
//
|
|
*ppOriginalConfig = NULL;
|
|
*phkeySvchost = NULL;
|
|
*ppszValueNameBuffer = NULL;
|
|
*pcchValueNameBuffer = 0;
|
|
*ppmszValueBuffer = NULL;
|
|
*pcbValueBuffer = 0;
|
|
|
|
const DWORD dwScmAccess = STANDARD_RIGHTS_REQUIRED |
|
|
SC_MANAGER_CONNECT |
|
|
SC_MANAGER_LOCK;
|
|
|
|
const DWORD dwSvcAccess = STANDARD_RIGHTS_REQUIRED |
|
|
SERVICE_QUERY_CONFIG |
|
|
SERVICE_CHANGE_CONFIG;
|
|
|
|
// Open the service and lock the service database so we can change
|
|
// the service's configuration.
|
|
//
|
|
HRESULT hr = pscm->HrOpenService (
|
|
psvc,
|
|
pszService,
|
|
WITH_LOCK,
|
|
dwScmAccess,
|
|
dwSvcAccess);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Query the service's current configuration in the event we
|
|
// need to revert what we set.
|
|
//
|
|
LPQUERY_SERVICE_CONFIG pOriginalConfig;
|
|
hr = psvc->HrQueryServiceConfig (&pOriginalConfig);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Open the svchost software key and query information
|
|
// about it like the length of the longest value name
|
|
// and longest value.
|
|
//
|
|
HKEY hkeySvchost;
|
|
hr = HrRegOpenKeyEx (
|
|
HKEY_LOCAL_MACHINE, REGSTR_PATH_SVCHOST,
|
|
KEY_READ | KEY_SET_VALUE,
|
|
&hkeySvchost);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD cchMaxValueNameLen;
|
|
DWORD cbMaxValueLen;
|
|
|
|
LONG lr = RegQueryInfoKeyW (hkeySvchost,
|
|
NULL, // lpClass
|
|
NULL, // lpcbClass
|
|
NULL, // lpReserved
|
|
NULL, // lpcSubKeys
|
|
NULL, // lpcbMaxSubKeyLen
|
|
NULL, // lpcbMaxClassLen
|
|
NULL, // lpcValues
|
|
&cchMaxValueNameLen,
|
|
&cbMaxValueLen,
|
|
NULL, // lpcbSecurityDescriptor
|
|
NULL // lpftLastWriteTime
|
|
);
|
|
hr = HRESULT_FROM_WIN32 (lr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Make sure the name buffer length (in bytes) is a
|
|
// multiple of sizeof(WCHAR). This is because we expect
|
|
// to use RegEnumValue which accepts and returns buffer
|
|
// size in characters. We tell it the the buffer
|
|
// capacity (in characters) is count of bytes divided
|
|
// by sizeof(WCHAR). So, to avoid any round off
|
|
// error (which would not occur in our favor) we make
|
|
// sure that the buffer size is a multiple of
|
|
// sizeof(WCHAR).
|
|
//
|
|
INT cbFraction = cbMaxValueLen % sizeof(WCHAR);
|
|
if (cbFraction)
|
|
{
|
|
cbMaxValueLen += sizeof(WCHAR) - cbFraction;
|
|
}
|
|
|
|
// Need room for the null terminator as RegQueryInfoKey
|
|
// doesn't return it.
|
|
//
|
|
cchMaxValueNameLen++;
|
|
|
|
// Allocate buffers for the longest value name and value
|
|
// data for our caller to use.
|
|
//
|
|
PWSTR pszValueNameBuffer = (PWSTR)
|
|
MemAlloc (cchMaxValueNameLen * sizeof(WCHAR));
|
|
|
|
PWSTR pmszValueBuffer = (PWSTR) MemAlloc (cbMaxValueLen);
|
|
|
|
if ((pszValueNameBuffer != NULL) &&
|
|
(pmszValueBuffer != NULL))
|
|
{
|
|
*ppOriginalConfig = pOriginalConfig;
|
|
*phkeySvchost = hkeySvchost;
|
|
|
|
*ppszValueNameBuffer = pszValueNameBuffer;
|
|
*pcchValueNameBuffer = cchMaxValueNameLen;
|
|
|
|
*ppmszValueBuffer = pmszValueBuffer;
|
|
*pcbValueBuffer = cbMaxValueLen;
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
RegCloseKey (hkeySvchost);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
MemFree (pOriginalConfig);
|
|
}
|
|
}
|
|
}
|
|
TraceError ("HrPrepareForSvchostEnum", hr);
|
|
return hr;
|
|
}
|
|
|
|
STDAPI
|
|
SvchostChangeSvchostGroup (
|
|
PCWSTR pszService,
|
|
PCWSTR pszNewGroup
|
|
)
|
|
{
|
|
Assert (pszService);
|
|
Assert (pszNewGroup);
|
|
|
|
static const WCHAR c_pszBasePath [] =
|
|
L"%SystemRoot%\\System32\\svchost.exe -k ";
|
|
|
|
// Validate the new group name by making sure it doesn't exceed
|
|
// MAX_PATH when combined with the base path.
|
|
//
|
|
if (!pszService || !pszNewGroup ||
|
|
!*pszService || !*pszNewGroup ||
|
|
(lstrlenW (c_pszBasePath) + lstrlenW (pszNewGroup) > MAX_PATH))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Form the new image path based on the base path and the new group
|
|
// name.
|
|
//
|
|
WCHAR pszImagePath [MAX_PATH + 1];
|
|
lstrcpyW (pszImagePath, c_pszBasePath);
|
|
lstrcatW (pszImagePath, pszNewGroup);
|
|
|
|
// Need to change the ImagePath of the service as well as the
|
|
// Svchost Group values. The implementation tries to ensure that
|
|
// both of these changes are made or neither of them are made.
|
|
//
|
|
|
|
// Prepare for the enumeration by setting up a few pieces of information
|
|
// first. HrPrepareForSvchostEnum sets up all of these variables.
|
|
//
|
|
// SCM is opened and locked, pszService is opened for config change.
|
|
//
|
|
CServiceManager scm;
|
|
CService svc;
|
|
|
|
// pszService's current configration is obtained in the event we
|
|
// need to rollback, we'll use this info to reset the ImagePath.
|
|
//
|
|
LPQUERY_SERVICE_CONFIG pOriginalConfig;
|
|
|
|
// hkeySvcHost is opened at REGSTR_PATH_SVCHOST and is used to
|
|
// enumerate the values under it.
|
|
//
|
|
HKEY hkeySvcHost;
|
|
|
|
// These buffers are allocated so that RegEnumValue will have a place
|
|
// to store what was enumerated.
|
|
//
|
|
PWSTR pszValueNameBuffer;
|
|
DWORD cchValueNameBuffer;
|
|
PWSTR pmszValueBuffer;
|
|
DWORD cbValueBuffer;
|
|
|
|
HRESULT hr = HrPrepareForSvchostEnum (
|
|
pszService,
|
|
&scm,
|
|
&svc,
|
|
&pOriginalConfig,
|
|
&hkeySvcHost,
|
|
&pszValueNameBuffer,
|
|
&cchValueNameBuffer,
|
|
&pmszValueBuffer,
|
|
&cbValueBuffer);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Set the new image path of the service.
|
|
//
|
|
hr = svc.HrSetImagePath (pszImagePath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// fAddNewValue will be set to FALSE if we've found an existing
|
|
// group name value.
|
|
//
|
|
BOOL fAddNewValue = TRUE;
|
|
BOOL fChanged;
|
|
|
|
// Now perform the enumeration. For each value enumerated,
|
|
// make sure the service name is included in the multi-sz
|
|
// for the valuename that matches the new group name. For all
|
|
// other values, make sure the service name is not included
|
|
// in the multi-sz.
|
|
//
|
|
DWORD dwIndex = 0;
|
|
do
|
|
{
|
|
DWORD dwType;
|
|
DWORD cchValueName = cchValueNameBuffer;
|
|
DWORD cbValue = cbValueBuffer;
|
|
|
|
hr = HrRegEnumValue (hkeySvcHost, dwIndex,
|
|
pszValueNameBuffer, &cchValueName,
|
|
&dwType,
|
|
(LPBYTE)pmszValueBuffer, &cbValue);
|
|
|
|
if (SUCCEEDED(hr) && (REG_MULTI_SZ == dwType))
|
|
{
|
|
// If we find a value that matches the group name,
|
|
// make sure the service is a part of the mutli-sz
|
|
// value.
|
|
//
|
|
if (0 == lstrcmpiW (pszNewGroup, pszValueNameBuffer))
|
|
{
|
|
// Since we found an existing group name, we don't
|
|
// need to add a new one.
|
|
//
|
|
fAddNewValue = FALSE;
|
|
|
|
PWSTR pmszNewValue;
|
|
|
|
hr = HrAddSzToMultiSz (pszService,
|
|
pmszValueBuffer,
|
|
STRING_FLAG_DONT_MODIFY_IF_PRESENT |
|
|
STRING_FLAG_ENSURE_AT_END,
|
|
0,
|
|
&pmszNewValue,
|
|
&fChanged);
|
|
|
|
if (SUCCEEDED(hr) && fChanged)
|
|
{
|
|
hr = HrRegSetMultiSz (hkeySvcHost,
|
|
pszNewGroup,
|
|
pmszNewValue);
|
|
|
|
MemFree (pmszNewValue);
|
|
}
|
|
}
|
|
|
|
// Otherwise, since the value does not match the group
|
|
// name, make sure the service is NOT part of the
|
|
// mutli-sz value.
|
|
//
|
|
else
|
|
{
|
|
RemoveSzFromMultiSz (pszService,
|
|
pmszValueBuffer, STRING_FLAG_REMOVE_ALL,
|
|
&fChanged);
|
|
|
|
if (fChanged)
|
|
{
|
|
hr = HrRegSetMultiSz (hkeySvcHost,
|
|
pszValueNameBuffer,
|
|
pmszValueBuffer);
|
|
}
|
|
}
|
|
}
|
|
else if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
dwIndex++;
|
|
}
|
|
while (S_OK == hr);
|
|
|
|
// If we need to add a new group name, do so.
|
|
//
|
|
if (SUCCEEDED(hr) && fAddNewValue)
|
|
{
|
|
// Add pszService to a empty multi-sz. This has the effect
|
|
// of creating a multi-sz from a single string.
|
|
//
|
|
PWSTR pmszNewValue;
|
|
|
|
hr = HrAddSzToMultiSz (pszService,
|
|
NULL,
|
|
STRING_FLAG_ENSURE_AT_END, 0,
|
|
&pmszNewValue, &fChanged);
|
|
if (S_OK == hr)
|
|
{
|
|
// We know that it should have been added, so assert
|
|
// that the multi-sz "changed".
|
|
//
|
|
Assert (fChanged);
|
|
|
|
// Add the new value by setting the multi-sz in the
|
|
// registry.
|
|
//
|
|
hr = HrRegSetMultiSz (hkeySvcHost,
|
|
pszNewGroup,
|
|
pmszNewValue);
|
|
|
|
MemFree (pmszNewValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey (hkeySvcHost);
|
|
|
|
MemFree (pmszValueBuffer);
|
|
MemFree (pszValueNameBuffer);
|
|
MemFree (pOriginalConfig);
|
|
}
|
|
TraceError ("SvchostChangeSvchostGroup", hr);
|
|
return hr;
|
|
}
|