windows-nt/Source/XPSP1/NT/net/config/netcfg/engine/ecomp.cpp

818 lines
22 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1999.
//
// File: E C O M P . C P P
//
// Contents: Implements the interface to a component's external data.
// External data is that data controlled (or placed) by
// PnP or the network class installer. Everything under a
// component's instance key is considered external data.
// (Internal data is that data we store in the persisted binary
// for the network configuration. See persist.cpp for
// code that deals with internal data.)
//
// Notes:
//
// Author: shaunco 15 Jan 1999
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "comp.h"
#include "ncsetup.h"
#include "util.h"
// constants
const WCHAR c_szHelpText[] = L"HelpText";
//+---------------------------------------------------------------------------
//
// Function: HrBuildBindNameFromBindForm
//
// Purpose: Build a bindname from a bindform and component parameters.
//
// Arguments:
// pszBindForm [in] The components bindform. This is read from
// the Ndi key. If the component did not specify
// it in its Ndi key, pass NULL.
// Class [in] The class of the component.
// dwCharacteristics [in] The characteristics of the component.
// pszServiceName [in] The components service name.
// pszInfId [in] The component (device) id.
// szInstanceGuid [in] The instance GUID of the component.
// ppszBindName [out] The returned bind string. This must be freed
// with LocalFree.
//
// Returns: HRESULT
//
// Author: shaunco 6 Jun 1997
// Modified: ckotze 21 Dec 2000
//
// Notes: The bindform contains replaceable parameters designed to
// be used with the FormatMessage API.
// %1 = pszServiceName
// %2 = pszInfId
// %3 = szInstanceGuid
//
HRESULT
HrBuildBindNameFromBindForm (
IN PCWSTR pszBindForm,
IN NETCLASS Class,
IN DWORD dwCharacteristics,
IN PCWSTR pszServiceName,
IN PCWSTR pszInfId,
IN const GUID& InstanceGuid,
OUT PWSTR* ppszBindName)
{
static const WCHAR c_szBindFormNet [] = L"%3";
static const WCHAR c_szBindFormNoService [] = L"%2";
static const WCHAR c_szBindFormDefault [] = L"%1";
WCHAR szInstanceGuid [c_cchGuidWithTerm];
INT cch;
DWORD dwRet = 0;
HRESULT hr = S_OK;
Assert (ppszBindName);
Assert (FIsValidNetClass(Class));
cch = StringFromGUID2 (
InstanceGuid,
szInstanceGuid,
c_cchGuidWithTerm);
Assert (c_cchGuidWithTerm == cch);
if (FIsPhysicalAdapter(Class, dwCharacteristics))
{
// netcards use the the instance guid only
// We disregard any bind form sent in.
Assert (szInstanceGuid && *szInstanceGuid);
pszBindForm = c_szBindFormNet;
}
else if (!pszBindForm || !*pszBindForm)
{
// Figure out which bindform to use since it wasn't specified.
//
if (FIsEnumerated(Class))
{
// Virtual adapters use the the instance guid only
Assert (szInstanceGuid && *szInstanceGuid);
pszBindForm = c_szBindFormNet;
}
else if (pszServiceName && *pszServiceName)
{
// use the service name if we have one
pszBindForm = c_szBindFormDefault;
}
else
{
// if no service, then use the component id
Assert (pszInfId && *pszInfId);
pszBindForm = c_szBindFormNoService;
}
}
AssertSz (pszBindForm && *pszBindForm, "Should have pszBindForm by now.");
// dwRet is either 0 or the number of chars in the resulting string. Since
// *ppszBindName is either NULL or a valid string, we get the last error if it's
// a NULL and ignore dwRet.
dwRet = DwFormatStringWithLocalAlloc (
pszBindForm, ppszBindName,
pszServiceName, pszInfId, szInstanceGuid);
if (*ppszBindName)
{
// Underscores are not allowed in the bind name so make a pass
// to remove them.
//
PWSTR pszScan = *ppszBindName;
while (NULL != (pszScan = wcschr (pszScan, L'_')))
{
wcscpy (pszScan, pszScan + 1);
}
}
else
{
DWORD dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
}
AssertSz (*ppszBindName,
"BuildBindNameFromBindForm: DwFormatStringWithLocalAlloc failed.");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: NcLoadRegUIString
//
// Purpose: Wrapper around SHLoadRegUIString, which is used to support MUI.
//
// Arguments: same as RegQueryValueEx with lpReserved and lpType removed
//
// Returns: If the function succeeds, the return value is ERROR_SUCCESS
//
// Notes: SHLoadRegUIString will read a string of the form
//
// @[path\]<dllname>,-<strId>
//
// The string with id <strId> is loaded from <dllname>. If no explicit
// path is provided then the DLL will be chosen according to pluggable UI
// specifications, if possible.
//
// If the registry string is not of the special form described here,
// SHLoadRegUIString will return the string intact.
//
//
LONG NcLoadRegUIString (
IN HKEY hkey,
IN PCWSTR lpValueName,
IN OUT LPBYTE lpData OPTIONAL,
IN OUT LPDWORD lpcbData)
{
const DWORD cchGrow = 256; // grows at 256 of WCHAR a time
DWORD cchBuffer = 0; // buffer size in number of WCHAR
HRESULT hr = S_OK;
LONG lr = ERROR_SUCCESS;
LPWSTR pwszBuffer = NULL; // buffer for the WCHAR string
DWORD cbBuffer = 0; // the real buffer size in bytes
if ( (NULL == hkey) || (NULL == lpValueName) )
{
return ERROR_BAD_ARGUMENTS;
}
// The lpcbData parameter can be NULL only if lpData is NULL.
if ( (NULL == lpcbData) && (NULL != lpData) )
{
return ERROR_BAD_ARGUMENTS;
}
if ( (NULL == lpcbData) && (lpData == NULL) )
{
// no operation
return ERROR_SUCCESS;
}
Assert (lpcbData);
if ( (*lpcbData > 0) && lpData && IsBadWritePtr(lpData, *lpcbData))
{
return ERROR_BAD_ARGUMENTS;
}
do
{
if (pwszBuffer)
{
// free the last allocated buffer
MemFree((LPVOID) pwszBuffer);
}
// allocate a larger buffer for the string
cchBuffer += cchGrow;
pwszBuffer = (LPWSTR) MemAlloc (cchBuffer * sizeof(WCHAR));
if (pwszBuffer == NULL)
{
return (ERROR_OUTOFMEMORY);
}
// load the MUI enabled string from the registry
// NOTE: for the buffer size, this API takes number of characters including NULL
// character, not number of bytes for the buffer.
// SHLoadRegUIStringW(HKEY hkey, LPCWSTR pszValue, IN OUT LPWSTR pszOutBuf, IN UINT cchOutBuf)
hr = SHLoadRegUIStringW (hkey, lpValueName, (LPWSTR)pwszBuffer, cchBuffer);
if (FAILED(hr))
{
lr = ERROR_FUNCTION_FAILED;
goto Exit;
}
// Unfortunately, SHLoadRegUIString doesn't have a way to query the
// buffer size, so we assume more data available. We'll loop around,
// grow the buffer, and try again.
} while ( wcslen(pwszBuffer) == (cchBuffer - 1) ); // retry if the last buffer is fully used
Assert (ERROR_SUCCESS == lr);
// the actual buffer size requirement in bytes
cbBuffer = (wcslen(pwszBuffer) + 1 ) * sizeof(WCHAR);
// If lpData is NULL, and lpcbData is non-NULL, the function returns ERROR_SUCCESS,
// and stores the size of the data, in bytes, in the variable pointed to by lpcbData.
// This lets an application determine the best way to allocate a buffer for
// the value's data.
if ( (NULL == lpData) && lpcbData )
{
*lpcbData = cbBuffer;
goto Exit;
}
// If the buffer specified by lpData parameter is not large enough to hold the data,
// the function returns the value ERROR_MORE_DATA, and stores the required buffer size,
// in bytes, into the variable pointed to by lpcbData. In this case, the contents of
// the lpValue buffer are undefined.
if (cbBuffer > *lpcbData)
{
*lpcbData = cbBuffer;
lr = ERROR_MORE_DATA;
goto Exit;
}
// transfer values
*lpcbData = cbBuffer;
if (lpData)
{
CopyMemory(lpData, (LPBYTE) pwszBuffer, cbBuffer);
}
Exit:
if (pwszBuffer)
{
MemFree((LPVOID) pwszBuffer);
}
return (lr);
}
//
// Query a value from the registry and ensure it is of the type we expect
// it to be. When calling RegQueryValueEx, we don't care to know what
// the type is, only to know if it doesn't match what we expect it to be.
// If the value is not of dwType, return ERROR_INVALID_DATATYPE.
//
LONG
RegQueryValueType (
IN HKEY hkey,
IN PCWSTR pszValueName,
IN DWORD dwType,
OUT BYTE* pbData OPTIONAL,
IN OUT DWORD* pcbData)
{
LONG lr;
DWORD dwTypeQueried;
lr = RegQueryValueExW (hkey, pszValueName, NULL, &dwTypeQueried, pbData, pcbData);
if (!lr && (dwType != dwTypeQueried))
{
lr = ERROR_INVALID_DATATYPE;
}
return lr;
}
//
// Read a REG_SZ that is expected to represent a GUID and convert it
// to its GUID representation. If the value does not seem to be a GUID,
// return ERROR_INVALID_DATATYPE.
//
// hkey is the parent key to read from and pszValueName is the name of the
// value whose data is expected to be a GUID in string form.
// pguidData points to a buffer to receive the GUID. If pguidData is NULL,
// no data will be returned, but the size of the buffer required will be
// stored at the DWORD pointed to by pcbData.
//
// On input, *pcbData is the size (in bytes) of the buffer pointed to
// by pguidData. On output, *pcbData is the size (in bytes) required to hold
// the data.
//
LONG
RegQueryGuid (
IN HKEY hkey,
IN PCWSTR pszValueName,
OUT GUID* pguidData OPTIONAL,
IN OUT DWORD* pcbData
)
{
LONG lr;
HRESULT hr;
WCHAR szGuid [c_cchGuidWithTerm];
DWORD cbDataIn;
DWORD cbData;
Assert (pcbData);
cbDataIn = *pcbData;
*pcbData = 0;
// Get the string form of the guid and store it in szGuid.
//
cbData = sizeof (szGuid);
lr = RegQueryValueType (hkey, pszValueName, REG_SZ, (PBYTE)szGuid, &cbData);
if (!lr)
{
GUID guid;
// Convert the string to a GUID. If this fails, the data is invalid
// and we will return such.
//
hr = IIDFromString (szGuid, &guid);
if (S_OK != hr)
{
lr = ERROR_INVALID_DATATYPE;
}
if (!lr)
{
// The data looks to be a GUID, so we'll return the size
// and the data if the caller wants it.
//
*pcbData = sizeof(GUID);
if (pguidData)
{
if (cbDataIn >= sizeof(GUID))
{
*pguidData = guid;
}
else
{
lr = ERROR_MORE_DATA;
}
}
}
}
// If querying for the string form of the guid returned ERROR_MORE_DATA,
// it means that the data is not a GUID.
//
else if (ERROR_MORE_DATA == lr)
{
lr = ERROR_INVALID_DATATYPE;
}
return lr;
}
// Used as input to RegQueryValues.
//
struct REGVALINFO
{
// The name of the subkey under which this registry value lives.
// Set this to NULL if this registry value lives under the same key
// as the previous registry value in the array of this structure.
//
PCWSTR pszSubkey;
// The name of the registry value.
//
PCWSTR pszValueName;
// The type of the registry value. One of REG_SZ, REG_DWORD, etc.
// REG_GUID is also supported.
//
DWORD dwType;
// The byte offset of the output pointer within the pbPointers
// array to store the pointer to the queried data.
//
UINT cbOffset;
};
#define REG_GUID ((DWORD)-5)
//
// Query a batch of values from the registry. The number of values to query
// is given by cValues. Information about the values is given through
// an array of REGVALINFO structures. The data for the values is stored
// in the caller-supplied buffer pointed to by pbBuf. The caller also
// supplies an array of pointers which will be set to point within pbBuf
// to the data for each value. This array is must also have cValues elements.
//
// If a value does not exist, its corresponding pointer value in the
// pbPointers array is set to NULL. This allows the caller to know whether
// the value existed or not.
//
LONG
RegQueryValues (
IN HKEY hkeyRoot,
IN ULONG cValues,
IN const REGVALINFO* aValueInfo,
OUT BYTE* pbPointers,
OUT BYTE* pbBuf OPTIONAL,
IN OUT ULONG* pcbBuf)
{
LONG lr;
ULONG cbBufIn;
ULONG cbBufRequired;
ULONG cbData;
ULONG cbPad;
BYTE *pData;
const REGVALINFO* pInfo;
HKEY hkey;
HRESULT hr;
Assert (hkeyRoot);
Assert (pcbBuf);
Assert (((ULONG_PTR)pbBuf & (sizeof(PVOID)-1)) == 0);
// On input, *pcbBuf is the number of bytes available in pbBuf.
//
cbBufIn = *pcbBuf;
cbBufRequired = 0;
hkey = hkeyRoot;
for (pInfo = aValueInfo; cValues; pInfo++, cValues--)
{
// Make sure we have the hkey we want.
//
if (pInfo->pszSubkey)
{
if (hkey != hkeyRoot)
{
RegCloseKey (hkey);
}
lr = RegOpenKeyEx (hkeyRoot, pInfo->pszSubkey, 0, KEY_READ, &hkey);
if (lr)
{
continue;
}
}
cbPad = cbBufRequired & (sizeof(PVOID)-1);
if (cbPad !=0) {
//
// The current buffer offset is misaligned. Increment it so that
// it is properly aligned.
//
cbPad = sizeof(PVOID) - cbPad;
cbBufRequired += cbPad;
}
if (pbBuf != NULL) {
//
// The caller supplied a buffer, so calculate a pointer to the
// current position within it.
//
pData = pbBuf + cbBufRequired;
} else {
pData = NULL;
}
//
// Set cbData to the amount of data remaining in the buffer.
//
if (cbBufIn > cbBufRequired) {
cbData = cbBufIn - cbBufRequired;
} else {
//
// No room left, pass in a NULL buffer pointer too.
//
cbData = 0;
pData = NULL;
}
//
// Perform the query based on the desired type.
//
if (REG_GUID == pInfo->dwType)
{
lr = RegQueryGuid (hkey, pInfo->pszValueName, (GUID*)pData, &cbData);
}
else if ( (REG_SZ == pInfo->dwType) && (!wcscmp(pInfo->pszValueName, c_szHelpText)) )
{
// Bug# 310358, load MUI string if necessary
lr = NcLoadRegUIString(hkey, pInfo->pszValueName, pData, &cbData);
}
else
{
lr = RegQueryValueType (hkey, pInfo->pszValueName,
pInfo->dwType, pData, &cbData);
}
if (ERROR_SUCCESS == lr || ERROR_MORE_DATA == lr) {
//
// cbData contains the amount of data that is available. Update
// the buffer size required to contain all of the data.
//
cbBufRequired += cbData;
} else {
//
// The call failed for some reason other than ERROR_MORE_DATA,
// back out the alignment padding from cbBufRequired.
//
cbBufRequired -= cbPad;
}
if (ERROR_SUCCESS == lr && pData != NULL) {
//
// Data was retrieved into our buffer. Store the pointer to the
// data.
//
*((BYTE**)(pbPointers + pInfo->cbOffset)) = pData;
}
}
if (hkey != hkeyRoot)
{
RegCloseKey (hkey);
}
*pcbBuf = cbBufRequired;
if (cbBufRequired <= cbBufIn)
{
lr = ERROR_SUCCESS;
}
else
{
lr = (pbBuf) ? ERROR_MORE_DATA : ERROR_SUCCESS;
}
return lr;
}
LONG
RegQueryValuesWithAlloc (
IN HKEY hkeyRoot,
IN ULONG cValues,
IN const REGVALINFO* aValueInfo,
OUT BYTE* pbPointers,
OUT BYTE** ppbBuf,
IN OUT ULONG* pcbBuf)
{
LONG lr;
ULONG cbBuf;
ULONG cbBufConfirm;
*ppbBuf = NULL;
*pcbBuf = 0;
cbBuf = 0;
lr = RegQueryValues (hkeyRoot, cValues, aValueInfo,
pbPointers, NULL, &cbBuf);
if (!lr)
{
BYTE* pbBuf;
lr = ERROR_OUTOFMEMORY;
pbBuf = (BYTE*)MemAlloc (cbBuf);
if (pbBuf)
{
cbBufConfirm = cbBuf;
lr = RegQueryValues (hkeyRoot, cValues, aValueInfo,
pbPointers, pbBuf, &cbBufConfirm);
if (!lr)
{
Assert (cbBufConfirm == cbBuf);
*ppbBuf = pbBuf;
*pcbBuf = cbBuf;
}
else
{
MemFree (pbBuf);
}
}
}
return lr;
}
HRESULT
CExternalComponentData::HrEnsureExternalDataLoaded ()
{
if (m_fInitialized)
{
return m_hrLoadResult;
}
//$PERF: We can selectively prune certain rows out of this table under
// certain conditions. e.g. Enumerated components don't have Clsid or
// CoServices.
//
static const REGVALINFO aValues[] =
{
{ NULL, L"Description", REG_SZ, ECD_OFFSET(m_pszDescription) },
{ L"Ndi",
L"Clsid", REG_GUID, ECD_OFFSET(m_pNotifyObjectClsid) },
{ NULL, L"Service", REG_SZ, ECD_OFFSET(m_pszService) },
{ NULL, L"CoServices", REG_MULTI_SZ, ECD_OFFSET(m_pmszCoServices) },
{ NULL, L"BindForm", REG_SZ, ECD_OFFSET(m_pszBindForm) },
{ NULL, c_szHelpText, REG_SZ, ECD_OFFSET(m_pszHelpText) },
{ L"Ndi\\Interfaces",
L"LowerRange", REG_SZ, ECD_OFFSET(m_pszLowerRange) },
{ NULL, L"LowerExclude", REG_SZ, ECD_OFFSET(m_pszLowerExclude) },
{ NULL, L"UpperRange", REG_SZ, ECD_OFFSET(m_pszUpperRange) },
{ NULL, L"FilterMediaTypes",REG_SZ, ECD_OFFSET(m_pszFilterMediaTypes) },
};
// Get our containing component pointer so we can open it's
// instance key.
//
CComponent* pThis;
pThis = CONTAINING_RECORD(this, CComponent, Ext);
// Open the instance key of the component.
//
HRESULT hr;
HKEY hkeyInstance;
HDEVINFO hdi;
SP_DEVINFO_DATA deid;
hr = pThis->HrOpenInstanceKey (KEY_READ, &hkeyInstance, &hdi, &deid);
if (S_OK == hr)
{
LONG lr;
PVOID pvBuf;
ULONG cbBuf;
lr = RegQueryValuesWithAlloc (hkeyInstance, celems(aValues), aValues,
(BYTE*)this, (BYTE**)&pvBuf, &cbBuf);
if (!lr)
{
// Set our buffer markers.
//
m_pvBuffer = pvBuf;
m_pvBufferLast = (BYTE*)pvBuf + cbBuf;
// HrOpenInstanceKey may succeed but return a NULL hdi for
// enumerated components when the real instance key does not
// exist. This happens when the class installer removes the
// instance key and calls us to remove its bindings.
//
if (hdi && FIsEnumerated (pThis->Class()))
{
hr = HrSetupDiGetDeviceName (hdi, &deid,
(PWSTR*)&m_pszDescription);
}
if (S_OK == hr)
{
hr = HrBuildBindNameFromBindForm (
m_pszBindForm,
pThis->Class(),
pThis->m_dwCharacter,
m_pszService,
pThis->m_pszInfId,
pThis->m_InstanceGuid,
(PWSTR*)&m_pszBindName);
}
}
else
{
hr = HRESULT_FROM_WIN32(lr);
ZeroMemory (this, sizeof(*this));
}
SetupDiDestroyDeviceInfoListSafe (hdi);
RegCloseKey (hkeyInstance);
}
// Only perform initialization once, regardless of whether it succeeds
// or not.
//
m_fInitialized = TRUE;
m_hrLoadResult = hr;
TraceHr (ttidError, FAL, m_hrLoadResult, FALSE,
"CExternalComponentData::HrEnsureExternalDataLoaded (%S)",
pThis->PszGetPnpIdOrInfId());
return m_hrLoadResult;
}
BOOL
CExternalComponentData::FLoadedOkayIfLoadedAtAll () const
{
// Because m_hrLoadResult is S_OK even if we are not initialized,
// (i.e. if the component's data is not loaded) we can just check
// m_hrLoadResult without needing to check m_fInitialized.
//
return (S_OK == m_hrLoadResult);
}
VOID
CExternalComponentData::FreeDescription ()
{
// If m_pszDescription is not pointing somewhere in our buffer
// it means it is using a separate allocation. (Because it was
// changed.)
//
if ((m_pszDescription < (PCWSTR)m_pvBuffer) ||
(m_pszDescription > (PCWSTR)m_pvBufferLast))
{
MemFree ((VOID*)m_pszDescription);
}
m_pszDescription = NULL;
}
VOID
CExternalComponentData::FreeExternalData ()
{
LocalFree ((VOID*)m_pszBindName);
FreeDescription();
MemFree (m_pvBuffer);
}
HRESULT
CExternalComponentData::HrReloadExternalData ()
{
HRESULT hr;
FreeExternalData ();
ZeroMemory (this, sizeof(*this));
hr = HrEnsureExternalDataLoaded ();
TraceHr (ttidError, FAL, hr, FALSE,
"CExternalComponentData::HrReloadExternalData");
return hr;
}
HRESULT
CExternalComponentData::HrSetDescription (
PCWSTR pszNewDescription)
{
HRESULT hr;
Assert (pszNewDescription);
FreeDescription();
hr = E_OUTOFMEMORY;
m_pszDescription = (PWSTR)MemAlloc (CbOfSzAndTerm(pszNewDescription));
if (m_pszDescription)
{
wcscpy ((PWSTR)m_pszDescription, pszNewDescription);
hr = S_OK;
}
return hr;
}