478 lines
12 KiB
C++
478 lines
12 KiB
C++
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
props.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the implementation of the property search class
|
|
|
|
Author:
|
|
|
|
Keith Lau (keithlau@microsoft.com)
|
|
|
|
Revision History:
|
|
|
|
keithlau 07/05/97 created
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "dbgtrace.h"
|
|
#include "props.h"
|
|
|
|
// =================================================================
|
|
// Implementation of CPropertyTable
|
|
//
|
|
int __cdecl CompareProperties(const void *pElem1, const void *pElem2)
|
|
{
|
|
LPPROPERTY_ITEM pProp1 = (LPPROPERTY_ITEM) pElem1;
|
|
LPPROPERTY_ITEM pProp2 = (LPPROPERTY_ITEM) pElem2;
|
|
|
|
int iResult = 0;
|
|
|
|
TraceFunctEnter("CompareProperties");
|
|
|
|
//
|
|
// we want to force english language comparisons here, since our API
|
|
// uses english words as property names
|
|
//
|
|
LCID lcidUSEnglish =
|
|
MAKELCID(
|
|
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
|
SORT_DEFAULT);
|
|
|
|
//
|
|
// comparison semantics:
|
|
// in our search code the key item is always set to the first item.
|
|
// the second item will always have cCharsToCompare set to 0 or an
|
|
// integer. If it is set to 0 then we know that we want to do a full
|
|
// string comparison. If it is set to a integer >= 1 then we only want
|
|
// to compare up to cCharsToCompare characters.
|
|
//
|
|
int c = (int) (pProp2->cCharsToCompare);
|
|
if (c == 0) {
|
|
c = -1;
|
|
} else {
|
|
// make sure that it is safe to compare c bytes of the key string.
|
|
// if not we know this isn't a match
|
|
if (lstrlen(pProp1->pszName) <= c) iResult = -1;
|
|
}
|
|
if (iResult == 0) {
|
|
iResult = CompareString(lcidUSEnglish, NORM_IGNORECASE,
|
|
pProp1->pszName, c,
|
|
pProp2->pszName, c) - 2;
|
|
}
|
|
|
|
DebugTrace(NULL, "Comparing <%s> and <%s> yields %d (c = %lu)",
|
|
pProp1->pszName,
|
|
pProp2->pszName,
|
|
iResult,
|
|
c);
|
|
|
|
TraceFunctLeave();
|
|
|
|
return(iResult);
|
|
}
|
|
|
|
//
|
|
// GetPropertyType - Looks for a named property in the property table
|
|
// and returns its type.
|
|
//
|
|
// Arguments
|
|
// szPropertyName - ANSI string of the property name
|
|
// pptPropertyType - Returns type of desired property value
|
|
// ppPropertyContext- Context of property
|
|
//
|
|
// Return values
|
|
//
|
|
// S_OK - Succeeded
|
|
// E_FAIL - Property not found
|
|
// E_ABORT - Widechar conversion failure (WCHAR only)
|
|
//
|
|
HRESULT CPropertyTable::GetPropertyType(LPCSTR szPropertyName,
|
|
LPDWORD pptPropertyType,
|
|
LPPROP_CTXT pPropertyContext)
|
|
{
|
|
LPPROPERTY_ITEM pItem;
|
|
HRESULT hrRes;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::GetPropertyType");
|
|
|
|
// Validation
|
|
_ASSERT(szPropertyName);
|
|
_ASSERT(pptPropertyType);
|
|
_ASSERT(pPropertyContext);
|
|
|
|
if (!szPropertyName || !pptPropertyType || !pPropertyContext)
|
|
return(E_INVALIDARG);
|
|
|
|
*pptPropertyType = PT_NONE;
|
|
pPropertyContext->fIsWideStr = FALSE;
|
|
|
|
// Find the property
|
|
pItem = SearchForProperty(szPropertyName);
|
|
|
|
// Found?
|
|
if (pItem)
|
|
{
|
|
// Return the base type regardless
|
|
*pptPropertyType = pItem->ptBaseType;
|
|
pPropertyContext->pItem = pItem;
|
|
hrRes = S_OK;
|
|
}
|
|
else
|
|
hrRes = DISP_E_MEMBERNOTFOUND;
|
|
|
|
TraceFunctLeave();
|
|
return(hrRes);
|
|
}
|
|
|
|
//
|
|
// GetProperty - Retrieves property information using an established context
|
|
//
|
|
// Arguments
|
|
// szPropertyName - ANSI string of the property name
|
|
// pvBuffer - Pointer to buffer to return property data
|
|
// in native format. If the context is returned
|
|
// by a Wide version of GetPropertyType, the returned
|
|
// string will be in Wide format.
|
|
// pdwBufferLen - Total length of receiving buffer, receives the length
|
|
// of returned data on successful return
|
|
//
|
|
// Return values
|
|
//
|
|
// S_OK - Succeeded
|
|
// E_ABORT - ANSI to UNICODE conversion failed
|
|
// E_INVALIDARG - Bad params
|
|
// E_OUTOFMEMORY - Not enough memory
|
|
// Plus other errors returned by the accessor
|
|
//
|
|
HRESULT CPropertyTable::GetProperty(LPPROP_CTXT pPropertyContext,
|
|
LPCSTR pszPropertyName,
|
|
LPVOID pvBuffer,
|
|
LPDWORD pdwBufferLen)
|
|
{
|
|
LPPROPERTY_ITEM pItem = pPropertyContext->pItem;
|
|
HRESULT hrRes;
|
|
LPVOID pvTempBuf;
|
|
DWORD dwOriginalLength;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::GetProperty");
|
|
|
|
// Validation
|
|
_ASSERT(pvBuffer);
|
|
_ASSERT(pdwBufferLen);
|
|
|
|
if (!pvBuffer || !pdwBufferLen)
|
|
return(E_INVALIDARG);
|
|
|
|
dwOriginalLength = *pdwBufferLen;
|
|
|
|
if (pItem)
|
|
{
|
|
// Is this type of access allowed?
|
|
if (pItem->fAccess & PA_READ)
|
|
{
|
|
// If we have a wide string property, we will use a scratch
|
|
// buffer, then we will convert it back to UNICODE when we
|
|
// got the value
|
|
if (pPropertyContext->fIsWideStr)
|
|
{
|
|
pvTempBuf = GetScratchBuffer(dwOriginalLength);
|
|
if (!pvTempBuf)
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
else
|
|
pvTempBuf = pvBuffer;
|
|
|
|
// Found the item, now call the accessor
|
|
_ASSERT(pItem->ptBaseType < PT_MAXPT);
|
|
hrRes = pItem->pfnGetAccessor((char *) pszPropertyName,
|
|
pItem->pContext,
|
|
pItem->pCacheData,
|
|
pvTempBuf,
|
|
pdwBufferLen);
|
|
|
|
if (SUCCEEDED(hrRes) && pPropertyContext->fIsWideStr)
|
|
{
|
|
// Convert it back to UNICODE
|
|
if (!MultiByteToWideChar(CP_ACP, 0,
|
|
(LPCSTR)pvTempBuf,
|
|
*pdwBufferLen,
|
|
(LPWSTR)pvBuffer,
|
|
dwOriginalLength))
|
|
return(E_ABORT);
|
|
}
|
|
}
|
|
else
|
|
hrRes = E_ACCESSDENIED;
|
|
}
|
|
else
|
|
hrRes = E_INVALIDARG;
|
|
|
|
TraceFunctLeave();
|
|
return(hrRes);
|
|
}
|
|
|
|
//
|
|
// SetProperty - Sets the named property to the specified value
|
|
//
|
|
// Arguments
|
|
// szPropertyName - ANSI string of the property name
|
|
// pvBuffer - Pointer to buffer containing property value
|
|
// in native format
|
|
// dwBufferLen - Total length of data
|
|
// ptDesiredPropertyType - Type of property value data
|
|
//
|
|
// Return values
|
|
//
|
|
// S_OK - Succeeded
|
|
// E_FAIL - Property not found
|
|
// E_OUTOFMEMORY - Not enough memory
|
|
// E_ABORT - Widechar conversion failure (WCHAR only)
|
|
// Plus other errors returned by the accessor
|
|
//
|
|
HRESULT CPropertyTable::SetProperty(LPCSTR szPropertyName,
|
|
LPVOID pvBuffer,
|
|
DWORD dwBufferLen,
|
|
DWORD ptPropertyType)
|
|
{
|
|
LPPROPERTY_ITEM pItem;
|
|
HRESULT hrRes;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::SetProperty");
|
|
|
|
// Validation
|
|
_ASSERT(szPropertyName);
|
|
_ASSERT(pvBuffer);
|
|
_ASSERT(ptPropertyType < PT_MAXPT);
|
|
|
|
if (!szPropertyName || !pvBuffer)
|
|
return(E_INVALIDARG);
|
|
if (ptPropertyType >= PT_MAXPT)
|
|
return(E_INVALIDARG);
|
|
|
|
// Find the property
|
|
pItem = SearchForProperty(szPropertyName);
|
|
|
|
// Found?
|
|
if (pItem)
|
|
{
|
|
// This type of access allowed?
|
|
if (pItem->fAccess & PA_WRITE)
|
|
{
|
|
// Found the item, now call the accessor
|
|
hrRes = pItem->pfnSetAccessor((LPSTR)szPropertyName,
|
|
pItem->pContext,
|
|
pItem->pCacheData,
|
|
pvBuffer,
|
|
dwBufferLen,
|
|
ptPropertyType);
|
|
}
|
|
else
|
|
hrRes = E_ACCESSDENIED;
|
|
}
|
|
else
|
|
hrRes = DISP_E_MEMBERNOTFOUND;
|
|
|
|
TraceFunctLeave();
|
|
return(hrRes);
|
|
}
|
|
|
|
//
|
|
// this function allows one to set a property using a variant. if the
|
|
// variant isn't in our desired type then we'll try to convert it.
|
|
//
|
|
HRESULT CPropertyTable::SetProperty(LPCSTR szPropertyName,
|
|
VARIANT *pvarProperty)
|
|
{
|
|
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::SetPropertyAV");
|
|
|
|
LPPROPERTY_ITEM pItem;
|
|
HRESULT hr;
|
|
|
|
// Validation
|
|
_ASSERT(szPropertyName);
|
|
_ASSERT(pvarProperty);
|
|
|
|
if (!szPropertyName || !pvarProperty) return E_POINTER;
|
|
|
|
// Find the property
|
|
pItem = SearchForProperty(szPropertyName);
|
|
if (!pItem) return DISP_E_MEMBERNOTFOUND;
|
|
if (!(pItem->fAccess & PA_WRITE)) return E_ACCESSDENIED;
|
|
|
|
// get the variant type that we need for this property
|
|
short vtDesired;
|
|
switch (pItem->ptBaseType) {
|
|
case PT_STRING: vtDesired = VT_BSTR; break;
|
|
case PT_DWORD: vtDesired = VT_I4; break;
|
|
case PT_INTERFACE: vtDesired = VT_UNKNOWN; break;
|
|
default: return DISP_E_TYPEMISMATCH;
|
|
}
|
|
|
|
// convert the variant to one that we can deal with
|
|
CComVariant varDesired;
|
|
hr = varDesired.ChangeType(vtDesired, pvarProperty);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
switch (pItem->ptBaseType) {
|
|
case PT_STRING: {
|
|
DWORD cbstr = lstrlenW(varDesired.bstrVal);
|
|
|
|
if (!GetScratchBuffer(cbstr + 1)) return E_OUTOFMEMORY;
|
|
if (!WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
varDesired.bstrVal,
|
|
cbstr + 1,
|
|
m_szBuffer,
|
|
m_cBuffer,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
hr = pItem->pfnSetAccessor((LPSTR) szPropertyName,
|
|
pItem->pContext,
|
|
pItem->pCacheData,
|
|
m_szBuffer,
|
|
cbstr,
|
|
PT_STRING);
|
|
break;
|
|
}
|
|
case PT_DWORD: {
|
|
DWORD dwValue = (DWORD) varDesired.lVal;
|
|
hr = pItem->pfnSetAccessor((LPSTR) szPropertyName,
|
|
pItem->pContext,
|
|
pItem->pCacheData,
|
|
&dwValue,
|
|
sizeof(DWORD),
|
|
PT_DWORD);
|
|
break;
|
|
}
|
|
case PT_INTERFACE: {
|
|
hr = pItem->pfnSetAccessor((LPSTR) szPropertyName,
|
|
pItem->pContext,
|
|
pItem->pCacheData,
|
|
varDesired.punkVal,
|
|
sizeof(IUnknown *),
|
|
PT_INTERFACE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPropertyTable::CommitChanges()
|
|
{
|
|
HRESULT hrRes = S_OK;
|
|
|
|
// this gets set to NULL if our destructor is called
|
|
_ASSERT(m_pProperties != NULL);
|
|
|
|
// Call the commit accessor for each item in the property table
|
|
for (DWORD i = 0; i < m_dwProperties; i++)
|
|
if (m_pProperties[i].pfnCommitAccessor(
|
|
m_pProperties[i].pszName,
|
|
m_pProperties[i].pContext,
|
|
m_pProperties[i].pCacheData) != S_OK)
|
|
{
|
|
hrRes = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// If we successfully committed, we will invalidate the cache
|
|
m_pProperties[i].pfnInvalidateAccessor(
|
|
m_pProperties[i].pszName,
|
|
m_pProperties[i].pCacheData,
|
|
m_pProperties[i].ptBaseType);
|
|
}
|
|
|
|
return(hrRes);
|
|
}
|
|
|
|
HRESULT CPropertyTable::Invalidate()
|
|
{
|
|
HRESULT hrRes = S_OK;
|
|
|
|
// Call the invalidate accessor for each item in the property table
|
|
for (DWORD i = 0; i < m_dwProperties; i++)
|
|
m_pProperties[i].pfnInvalidateAccessor(
|
|
m_pProperties[i].pszName,
|
|
m_pProperties[i].pCacheData,
|
|
m_pProperties[i].ptBaseType);
|
|
return(S_OK);
|
|
}
|
|
|
|
//
|
|
// Private methods
|
|
//
|
|
LPVOID CPropertyTable::GetScratchBuffer(DWORD dwSizeDesired)
|
|
{
|
|
TraceFunctEnter("CProperty::GetScratchBuffer");
|
|
|
|
// See if any action is needed at all
|
|
if (m_cBuffer >= dwSizeDesired)
|
|
return(m_szBuffer);
|
|
|
|
// Reallocate ...
|
|
if (m_szBuffer != m_rgcBuffer)
|
|
LocalFree((HLOCAL)m_szBuffer);
|
|
//_VERIFY(LocalFree((HLOCAL)m_szBuffer) == (HLOCAL)NULL);
|
|
|
|
// Align buffer size to next page boundary, so we won't have
|
|
// to reallocate until something exceeds 4K
|
|
dwSizeDesired >>= 12;
|
|
dwSizeDesired++;
|
|
dwSizeDesired <<= 12;
|
|
|
|
m_szBuffer = (LPSTR)LocalAlloc(0, dwSizeDesired);
|
|
if (!m_szBuffer)
|
|
{
|
|
m_cBuffer = 0;
|
|
return(NULL);
|
|
}
|
|
m_cBuffer = dwSizeDesired;
|
|
|
|
TraceFunctLeave();
|
|
return(m_szBuffer);
|
|
}
|
|
|
|
LPPROPERTY_ITEM CPropertyTable::SearchForProperty(LPCSTR szPropertyName)
|
|
{
|
|
LPPROPERTY_ITEM pItem = NULL;
|
|
|
|
TraceFunctEnter("CPropertyTable::SearchForProperty");
|
|
|
|
// we always do a linear search. this allows us to optimize for the
|
|
// most common cases and allows us to make assumptions in CompareStrings
|
|
// about which element is the key element that makes CompareStrings
|
|
// faster. Since the table for NNTP only contains 6 elements this is
|
|
// not a perf hit.
|
|
DWORD i;
|
|
PROPERTY_ITEM KeyItem;
|
|
|
|
// Fill in the property name to look for
|
|
KeyItem.pszName = (LPSTR)szPropertyName;
|
|
|
|
// Linear search
|
|
pItem = NULL;
|
|
for (i = 0; i < m_dwProperties && pItem == NULL; i++) {
|
|
if (!CompareProperties(&KeyItem, m_pProperties + i)) {
|
|
pItem = m_pProperties + i;
|
|
}
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return(pItem);
|
|
}
|
|
|