|
|
//+---------------------------------------------------------------------------
//
// 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; }
|