//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1994 - 1999.
//
//  File:       propbase.hxx
//
//  Contents:   Headers for common property handling routines
//
//  Classes:    CUtlPropInfo, 
//              CUtlProps
//
//  History:    10-28-97        danleg    Created from monarch uprops.hpp
//
//----------------------------------------------------------------------------

#pragma once


//
// Constants and Static Struct 
//
#define DEFAULT_MACHINE     L"."

const ULONG DWORDSNEEDEDPERSET = 2;
const ULONG DWORDSNEEDEDTOTAL = DWORDSNEEDEDPERSET * 5;


//
// Macros
//

#define INRANGE(z,zmin,zmax) ( (zmin) <= (z) && (z) <= (zmax) )

//
//  Utility functions
//

// Bit array routines.
// Two versions: one for a single DWORD, one for arrays.
// Note that you pass in not a bit mask, but the bit number.
// i.e. dwBit = 0,1,2,3,4...31 instead of 1,2,4,8,0x10,0x20...

inline void SETBIT( DWORD &dwFlags, DWORD dwBit )
{
    dwFlags |= 1 << dwBit;
}

inline void CLEARBIT( DWORD &dwFlags, DWORD dwBit )
{
    dwFlags &= ~ (1<<dwBit);
}

inline DWORD TESTBIT( DWORD &dwFlags, DWORD dwBit )
{
    return (dwFlags & (1<<dwBit)) != 0;
}

// Routines based on array-of-DWORD.
// Use these like a bit array, from 0...n-1.

#define DivDword(dw) (dw >> 5)      // dw / 32 = dw / (sizeof(DWORD)*8)
#define ModDword(dw) (dw & (32-1))  // dw % 32
#define DwordSizeofBits(nBits) (nBits/32+1) // Use in array declarations

inline void SETBIT( DWORD rgdwFlags[], const DWORD dwBit )
{
    rgdwFlags[DivDword(dwBit)] |= 1 << ModDword(dwBit);
}

inline void CLEARBIT( DWORD rgdwFlags[], const DWORD dwBit )
{
    rgdwFlags[DivDword(dwBit)] &= ~( 1 << ModDword(dwBit) );
}

#define CLEARBITARRAY( rgdwFlags ) RtlZeroMemory( rgdwFlags, sizeof(rgdwFlags) )

inline DWORD TESTBIT( const DWORD rgdwFlags[], const DWORD dwBit )
{
    //old//Note: Not {0,1}, but from {0...2^32-1}.
    // Note: Now returns {0,1}.
    return ( rgdwFlags[DivDword(dwBit)] & ( 1 << ModDword(dwBit) ) ) != 0;
}

#undef DivDWord
#undef ModDWord

//
// Forward Declarations 
//

class CColumnIds;


//
// Constants and Static Struct 
//

// leave plenty of space for localization.
const ULONG     CCH_GETPROPERTYINFO_DESCRIP_BUFFER_SIZE = 256;

const DWORD     GETPROPINFO_ALLPROPIDS      = 0x0001;
const DWORD     GETPROPINFO_NOTSUPPORTED    = 0x0002;
const DWORD     GETPROPINFO_ERRORSOCCURRED  = 0x0004;
const DWORD     GETPROPINFO_VALIDPROP       = 0x0008;

const DWORD     GETPROP_ALLPROPIDS          = 0x0001;
const DWORD     GETPROP_NOTSUPPORTED        = 0x0002;
const DWORD     GETPROP_ERRORSOCCURRED      = 0x0004;
const DWORD     GETPROP_VALIDPROP           = 0x0008;
const DWORD     GETPROP_PROPSINERROR        = 0x0010;

const DWORD     SETPROP_BADOPTION           = 0x0001;
const DWORD     SETPROP_NOTSUPPORTED        = 0x0002;
const DWORD     SETPROP_VALIDPROP           = 0x0004;
const DWORD     SETPROP_ERRORS              = 0x0008;
const DWORD     SETPROP_COLUMN_LEVEL        = 0x0010;

const DWORD     DBINTERNFLAGS_NOCHANGE      = 0x00000000;
const DWORD     DBINTERNFLAGS_CHANGED       = 0x00000001;
const DWORD     DBINTERNFLAGS_CHEAPTOREQ    = 0x00000002;
const DWORD     DBINTERNFLAGS_INERROR       = 0x00000004;
const DWORD     DBINTERNFLAGS_INERRORSAVED  = 0x00000008;
const DWORD     DBINTERNFLAGS_TOTRUE        = 0x80000000;

// Additional Property Flags needed internally
const DWORD     DBPROPFLAGS_CHANGE          = 0x80000000;

// Flags for controlling UPROPSETS
const DWORD     UPROPSET_HIDDEN             = 0x00000001;
const DWORD     UPROPSET_PASSTHROUGH        = 0x00000002;

// Rules for GetPropertiesArgChk
const DWORD     ARGCHK_PROPERTIESINERROR    = 0x00000001;

// 
// STRUCTURE DEFINITIONS 
//

typedef struct tagPropColId* PPROPCOLID;

typedef struct tagUPROPVAL
{
    DBPROPOPTIONS   dwOption;
    DWORD           dwFlags;
    VARIANT         vValue;
} UPROPVAL;

typedef struct tagUPROPINFO
{
    DBPROPID    dwPropId;
    LPCWSTR     pwszDesc;
    VARTYPE     VarType;
    DBPROPFLAGS dwFlags;
} UPROPINFO;

typedef struct tagUPROP
{
    ULONG           cPropIds;
    UPROPINFO**     rgpUPropInfo;
    UPROPVAL*       pUPropVal;
} UPROP;

typedef struct tagUPROPSET
{
    const GUID* pPropSet;
    ULONG       cUPropInfo;
    const UPROPINFO*    pUPropInfo;
    DWORD       dwFlags;
} UPROPSET;


//+-------------------------------------------------------------------------
//
//  Class:      CUtlPropInfo
//
//  Purpose:    Base class for property storage
//
//  History:    11-12-97    danleg      Created from Monarch
//
//--------------------------------------------------------------------------

class CUtlPropInfo
{
public:

    //
    // Ctor / Dtor
    //
                    CUtlPropInfo();
          virtual   ~CUtlPropInfo() { };

    SCODE           GetPropertyInfo ( ULONG             cPropertySets, 
                                      const DBPROPIDSET rgPropertySets[], 
                                      ULONG *           pcPropertyInfoSets,
                                      DBPROPINFOSET **  prgPropertyInfoSets, 
                                      WCHAR **          ppDescBuffer );

    //
    // Get the count of propsets.
    //
    ULONG           GetUPropSetCount()      { return _cUPropSet; }

    //
    // Reset the count of propsets.
    //
    void            SetUPropSetCount( ULONG c ) { _cUPropSet = c; }


    //
    // Pure Virtual 
    //
    virtual SCODE   InitAvailUPropSets( 
                                      ULONG *           pcUPropSet,
                                      UPROPSET **       ppUPropSet, 
                                      ULONG *           pcElemPerSupported )=0;

    virtual SCODE   InitUPropSetsSupported(
                                      DWORD *           rgdwSupported )=0;

    //
    // Load a localized description
    //
    virtual ULONG   LoadDescription   ( LPCWSTR           pwszDesc, 
                                        PWSTR             pwszBuff,
                                        ULONG             cchBuff )=0;

protected:

    //
    // Initialization routine called called from constructors of derived 
    // classes.
    //
    void            FInit();

private:

    //
    // Count of UPropSet items
    //
    ULONG           _cUPropSet;

    //
    // Pointer to UPropset items
    //
    UPROPSET *      _pUPropSet;

    //
    // Count of UPropSet Indexes
    //
    ULONG           _cPropSetDex;

    //
    // Pointer to Array of UPropSet Index values
    //
    XArray<ULONG>   _xaiPropSetDex;

    //
    // Number of DWORDS per UPropSet to indicate supported UPropIds
    //
    ULONG           _cElemPerSupported;

    //
    // Pointer to array of DWORDs indicating supported UPropIds 
    //
    XArray<DWORD>   _xadwSupported;


    //
    // Member functions
    //

    // Retrieve the property id pointer 
    SCODE           GetUPropInfoPtr ( ULONG             iPropSetDex, 
                                      DBPROPID          dwPropertyId, 
                                      UPROPINFO **      ppUPropInfo );

    // Retrieve the property set indexes that match this property set.
    SCODE           GetPropertySetIndex( const GUID *     pPropertySet );

    // Determine the number of description buffers needed
    ULONG           CalcDescripBuffers( ULONG             cPropInfoSet,
                                        DBPROPINFOSET*    pPropInfoSet );

};


//+-------------------------------------------------------------------------
//
//  Class:      CUtlProps
//
//  Purpose:    Base class for property storage
//
//  History:    11-12-97    danleg      Created from Monarch
//
//--------------------------------------------------------------------------

class CUtlProps 
{
public: 

    //
    // Ctor / Dtor
    //
    CUtlProps( DWORD dwFlags = 0 );
    virtual ~CUtlProps();

    //
    // Fill default values again.
    //
    SCODE           FillDefaultValues(ULONG ulPropSetTarget = ULONG_MAX);

    //
    // Check the arguments for Retrieve Properties
    //
    void            GetPropertiesArgChk( const ULONG       cPropertySets, 
                                         const DBPROPIDSET rgPropertySets[],
                                         ULONG   *         pcProperties, 
                                         DBPROPSET **      prgProperties );

    //
    // Check the arguments for Set Properties
    //
    static void     SetPropertiesArgChk( const ULONG       cPropertySets, 
                                         const DBPROPSET   rgPropertySets[] );

    //
    // Retrieve Properties
    //
    SCODE           GetProperties   ( const ULONG       cPropertySets, 
                                      const DBPROPIDSET rgPropertySets[], 
                                      ULONG *           pcProperties, 
                                      DBPROPSET **      prgProperties );
    //
    // Set Properties
    //
    SCODE           SetProperties   ( const ULONG       cPropertySets, 
                                      const DBPROPSET   rgPropertySets[] );

    inline void     SetPropertyInError( 
                                      const ULONG       iPropSet, 
                                      const ULONG       iPropId );

    inline void     ClearPropertyInError( ) ;

    BOOL            IsPropSet       ( const GUID *      pguidPropSet, 
                                      DBPROPID          dwPropId );

    SCODE           GetPropValue    ( const GUID *      pguidPropSet, 
                                      DBPROPID          dwPropId, 
                                      VARIANT *         pvValue );

    SCODE           SetPropValue    ( const GUID *      pguidPropSet,
                                      DBPROPID          dwPropId, 
                                      VARIANT *         pvValue );

    SCODE           CopyUPropVal    ( ULONG             iPropSet, 
                                      UPROPVAL *        rgUPropVal );

    //
    // Virtuals functions
    //
    
    virtual SCODE   GetIndexofPropSet( const GUID *     pPropSet,
                                       ULONG *          pulCurSet );

    virtual SCODE   GetIndexofPropIdinPropSet( 
                                       ULONG            iCurSet, 
                                       DBPROPID         dwPropertyId, 
                                       ULONG *          piCurPropId );

    //
    // Pure Virtuals
    //
    
    virtual SCODE   InitAvailUPropSets( ULONG *         pcUPropSet, 
                                        UPROPSET **     ppUPropSet, 
                                        ULONG *         pcElemPerSupported )=0;

    virtual SCODE   GetDefaultValue ( ULONG             iPropSet, 
                                      DBPROPID          dwPropId, 
                                      DWORD *           dwOption, 
                                      VARIANT *         pVar )=0;

    virtual SCODE   IsValidValue    ( ULONG             iCurSet, 
                                      DBPROP *          pDBProp )=0;

    virtual SCODE   InitUPropSetsSupported( DWORD *     rgdwSupported) = 0;

    //
    // Inlined functions
    //
    inline ULONG    GetUPropSetCount()      { return _cUPropSet; }
    inline void     SetUPropSetCount( ULONG c ) { _cUPropSet = c; }

    //
    // NOTE: The following functions depend on all prior properties in the 
    // array being writable. This is because the UPROP array contains only 
    // writable elements, and the UPROPINFO array contains writable and 
    // read-only elements. If this is a problem, we would need to define 
    // which one it came from and add the appropriate asserts...
    //

    // Get DBPROPOPTIONS_xx
    inline DWORD    GetPropOption   ( ULONG             iPropSet, 
                                      ULONG             iProp );

    // Set DBPROPOPTIONS_xx
    inline void     SetPropOption   ( ULONG             iPropSet, 
                                      ULONG             iProp, 
                                      DWORD             dwOption );

    //
    // Determine if property is required and variant_true
    //
    inline BOOL     IsRequiredTrue  ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline DWORD    GetInternalFlags( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline void     AddInternalFlags( ULONG             iPropSet, 
                                      ULONG             iProp, 
                                      DWORD             dwFlag );

    inline void     RemoveInternalFlags ( 
                                      ULONG             iPropSet, 
                                      ULONG             iProp, 
                                      DWORD             dwFlag );

    inline VARIANT * GetVariant     ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline SCODE    SetVariant      ( ULONG             iPropSet, 
                                      ULONG             iProp, 
                                      VARIANT *         pv );

    inline void     SetValEmpty     ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline BOOL     IsEmpty         ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline void     SetValBool      ( ULONG             iPropSet, 
                                      ULONG             iProp, 
                                      VARIANT_BOOL      bVal );

    inline VARIANT_BOOL GetValBool  ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline void     SetValShort     ( ULONG             iPropSet, 
                                      ULONG             iProp,  
                                      SHORT             iVal );

    inline SHORT    GetValShort     ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline void     SetValLong      ( ULONG             iPropSet, 
                                      ULONG             iProp,
                                      LONG              lVal );

    inline LONG     GetValLong      ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline SCODE    SetValString    ( ULONG             iPropSet, 
                                      ULONG             iProp, 
                                      const WCHAR *     pwsz );

    inline const WCHAR * GetValString( 
                                      ULONG             iPropSet, 
                                      ULONG             iProp );

    inline const GUID *  GetGuid    ( ULONG             iPropSet);

    inline DWORD    GetPropID       ( ULONG             iPropSet, 
                                      ULONG             iProp );

    inline VARTYPE  GetExpectedVarType( ULONG           iPropSet, 
                                        ULONG           iProp );

    // Inline Methods
    inline void     CopyAvailUPropSets( ULONG *         pcUPropSet,
                                        UPROPSET **     ppUPropSet, 
                                        ULONG *         pcElemPerSupported );

    inline void     CopyUPropSetsSupported( 
                                        XArray<DWORD> & xadwSupported );

    inline void     CopyUPropInfo   ( ULONG             iPropSet, 
                                      XArray<UPROPINFO*> & xapUPropInfo );
protected:

    //
    // Number of DWORDS per UPropSet to indicate supported UPropIds
    //
    ULONG           _cElemPerSupported;

    //
    // Pointer to array of DWORDs indicating supported UPropIds 
    //
    XArray<DWORD>   _xadwSupported;

    //
    // Pointer to array of DWORDs indicating if property is in error
    //
    XArray<DWORD>   _xadwPropsInError;


    //
    // Initialization Method
    //
    virtual void    FInit           ( CUtlProps *       pCopyMe = NULL );

private:

    //
    // Count of UPropSet items
    //
    ULONG           _cUPropSet;

    //
    //Pointer to UPropset items
    //
    UPROPSET *      _pUPropSet;
    XArray<UPROP>   _xaUProp;

    //
    // Count of Hidden items
    //
    ULONG           _cUPropSetHidden;

    //
    // Configuration flags
    //
    DWORD           _dwFlags;

    //
    // Count of UPropSet Indexes
    //
    ULONG           _cPropSetDex;

    //
    // Pointer to Array of UPropSet Index values
    //
    XArray<ULONG>   _xaiPropSetDex;

    void            FreeMemory      ( );

    ULONG           GetCountofWritablePropsInPropSet( 
                                      ULONG             iPropSet );

    ULONG           GetUPropValIndex( ULONG             iCurset, 
                                      DBPROPID          dwPropId);

    SCODE           SetProperty     ( ULONG             iCurSet,
                                      ULONG             iCurProp, 
                                      DBPROP *          pDBProp);

    //
    // Retrieve the property set indexes that 
    // match this property set.
    //
    SCODE           GetPropertySetIndex( GUID *         pPropertySet );

};


// --------------------------------------------------------------------------
// -------------------- I N L I N E  F U N C T I O N S ----------------------
// --------------------------------------------------------------------------
inline void CUtlProps::CopyAvailUPropSets
    (
    ULONG* pcUPropSet,
    UPROPSET** ppUPropSet, 
    ULONG* pcElemPerSupported
    )
{
    Win4Assert( pcUPropSet && ppUPropSet && pcElemPerSupported );
    *pcUPropSet = _cUPropSet;
    *ppUPropSet = _pUPropSet;
    *pcElemPerSupported = _cElemPerSupported;
}


inline void CUtlProps::CopyUPropSetsSupported
    (
    XArray<DWORD> & xadwSupported
    )
{
    // if the passed in array already has elements, we will leak
    Win4Assert( xadwSupported.IsNull() );
    xadwSupported.Init( _xadwSupported );
    
    //RtlCopyMemory( xadwSupported.GetPointer(), 
    //               _xadwSupported.GetPointer(), 
    //               _cUPropSet * _cElemPerSupported * sizeof(DWORD) ); 
}

inline void CUtlProps::CopyUPropInfo
    (
    ULONG                  iPropSet,
    XArray<UPROPINFO*> &   xapUPropInfo
    )
{
    Win4Assert( xapUPropInfo.IsNull() );
    Win4Assert( iPropSet < _cUPropSet );
    
    xapUPropInfo.Init( _xaUProp[iPropSet].cPropIds );
    RtlCopyMemory( xapUPropInfo.GetPointer(), 
                   _xaUProp[iPropSet].rgpUPropInfo, 
                   xapUPropInfo.SizeOf() );
}

inline void CUtlProps::ClearPropertyInError() 
{ 
    Win4Assert( !_xadwPropsInError.IsNull() );
    RtlZeroMemory( _xadwPropsInError.GetPointer(), _cUPropSet * _cElemPerSupported * sizeof(DWORD) ); 
};

// Save a few lines by defining a common set of asserts.
#define is_proper_range \
    (  (iPropSet < _cUPropSet)                      \
    && (iProp < _pUPropSet[iPropSet].cUPropInfo)    \
    && (iProp < _xaUProp[iPropSet].cPropIds) )

// Set Property Ids that should be in Error
inline void CUtlProps::SetPropertyInError
    (
    const ULONG iPropSet,
    const ULONG iProp    
    )
{ 
    SETBIT(&(_xadwPropsInError[iPropSet * _cElemPerSupported]), iProp);
};

// determine if the property is Required and VARIANT_TRUE
inline BOOL CUtlProps::IsRequiredTrue(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    Win4Assert(_xaUProp[iPropSet].pUPropVal[iProp].vValue.vt == VT_BOOL);
    Win4Assert(V_BOOL(&_xaUProp[iPropSet].pUPropVal[iProp].vValue) == VARIANT_TRUE
    ||     V_BOOL(&_xaUProp[iPropSet].pUPropVal[iProp].vValue) == VARIANT_FALSE);

    return( !(_xaUProp[iPropSet].pUPropVal[iProp].dwFlags & DBINTERNFLAGS_INERROR) &&
            (_xaUProp[iPropSet].pUPropVal[iProp].dwOption == DBPROPOPTIONS_REQUIRED) &&
            (V_BOOL(&_xaUProp[iPropSet].pUPropVal[iProp].vValue) == VARIANT_TRUE) );
};

// Get DBPROPOPTIONS_xx
inline DWORD CUtlProps::GetPropOption(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    return _xaUProp[iPropSet].pUPropVal[iProp].dwOption;
}

inline void CUtlProps::SetPropOption(ULONG iPropSet, ULONG iProp, DWORD dwOption)
{
    Win4Assert( is_proper_range );
    _xaUProp[iPropSet].pUPropVal[iProp].dwOption = dwOption;
}

inline DWORD CUtlProps::GetInternalFlags(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    return _xaUProp[iPropSet].pUPropVal[iProp].dwFlags;
}

inline void CUtlProps::AddInternalFlags(ULONG iPropSet, ULONG iProp, DWORD dwFlags)
{
    Win4Assert( is_proper_range );
    _xaUProp[iPropSet].pUPropVal[iProp].dwFlags |= dwFlags;
}

inline void CUtlProps::RemoveInternalFlags(ULONG iPropSet, ULONG iProp, DWORD dwFlags)              
{
    Win4Assert( is_proper_range );
    _xaUProp[iPropSet].pUPropVal[iProp].dwFlags &= ~dwFlags;
}

inline VARIANT * CUtlProps::GetVariant(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    return & _xaUProp[iPropSet].pUPropVal[iProp].vValue;
}

inline SCODE CUtlProps::SetVariant(ULONG iPropSet, ULONG iProp, VARIANT *pv )
{
    Win4Assert( is_proper_range );
    // Does VariantClear first.
    return VariantCopy( &_xaUProp[iPropSet].pUPropVal[iProp].vValue, pv );
}

inline void CUtlProps::SetValEmpty(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    VariantClear( &_xaUProp[iPropSet].pUPropVal[iProp].vValue );
}

inline BOOL CUtlProps::IsEmpty(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    return ( _xaUProp[iPropSet].pUPropVal[iProp].vValue.vt == VT_EMPTY);
}

inline void CUtlProps::SetValBool(ULONG iPropSet, ULONG iProp, VARIANT_BOOL bVal )
{
    Win4Assert( is_proper_range );
    // Note that we accept any "true" value.
    VariantClear(&_xaUProp[iPropSet].pUPropVal[iProp].vValue);
    _xaUProp[iPropSet].pUPropVal[iProp].vValue.vt = VT_BOOL;
    V_BOOL(&_xaUProp[iPropSet].pUPropVal[iProp].vValue) 
        = (bVal ? VARIANT_TRUE : VARIANT_FALSE);
}

inline VARIANT_BOOL CUtlProps::GetValBool(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    Win4Assert(_xaUProp[iPropSet].pUPropVal[iProp].vValue.vt == VT_BOOL);
    return V_BOOL(&_xaUProp[iPropSet].pUPropVal[iProp].vValue);
}

inline void CUtlProps::SetValShort(ULONG iPropSet, ULONG iProp, SHORT iVal )
{
    Win4Assert( is_proper_range );
    VariantClear(&_xaUProp[iPropSet].pUPropVal[iProp].vValue);
    _xaUProp[iPropSet].pUPropVal[iProp].vValue.vt = VT_I2;
    _xaUProp[iPropSet].pUPropVal[iProp].vValue.iVal = iVal;
}

inline SHORT CUtlProps::GetValShort(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    Win4Assert(_xaUProp[iPropSet].pUPropVal[iProp].vValue.vt == VT_I2);
    return _xaUProp[iPropSet].pUPropVal[iProp].vValue.iVal;
}

inline void CUtlProps::SetValLong(ULONG iPropSet, ULONG iProp, LONG lVal )
{
    Win4Assert( is_proper_range );
    VariantClear(&_xaUProp[iPropSet].pUPropVal[iProp].vValue);
    _xaUProp[iPropSet].pUPropVal[iProp].vValue.vt = VT_I4;
    _xaUProp[iPropSet].pUPropVal[iProp].vValue.lVal = lVal;
}

// Get value as long
inline LONG CUtlProps::GetValLong(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    Win4Assert(_xaUProp[iPropSet].pUPropVal[iProp].vValue.vt == VT_I4);
    return _xaUProp[iPropSet].pUPropVal[iProp].vValue.lVal;
}

// Set value as string
inline SCODE CUtlProps::SetValString(ULONG iPropSet, ULONG iProp, const WCHAR *pwsz )
{
    Win4Assert( is_proper_range );
    VARIANT *pv = &_xaUProp[iPropSet].pUPropVal[iProp].vValue;
    VariantClear(pv);
    pv->bstrVal = SysAllocString(pwsz);
    if ( pv->bstrVal )
        pv->vt = VT_BSTR;
    else
        return E_FAIL;

    // See if this was used for non-string type.
    // Typically this is an easy way to pass integer as a string.
    if ( GetExpectedVarType(iPropSet,iProp) == VT_BSTR )
        return NOERROR;
    if ( pwsz[0] != L'\0' )
        return VariantChangeType( pv, pv, 0, GetExpectedVarType(iPropSet,iProp) );

    // Set to "", which for non-string means empty.
    SysFreeString(pv->bstrVal);
    pv->vt = VT_EMPTY;
    return NOERROR;
}

inline const WCHAR * CUtlProps::GetValString(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    Win4Assert(_xaUProp[iPropSet].pUPropVal[iProp].vValue.vt == VT_BSTR);
    return _xaUProp[iPropSet].pUPropVal[iProp].vValue.bstrVal;
}

inline const GUID * CUtlProps::GetGuid(ULONG iPropSet)
{
    Win4Assert(iPropSet < _cUPropSet);
    return _pUPropSet[iPropSet].pPropSet;
}

inline DWORD CUtlProps::GetPropID(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    return _pUPropSet[iPropSet].pUPropInfo[iProp].dwPropId;
}

inline VARTYPE CUtlProps::GetExpectedVarType(ULONG iPropSet, ULONG iProp)
{
    Win4Assert( is_proper_range );
    return _pUPropSet[iPropSet].pUPropInfo[iProp].VarType;
}

#undef is_proper_range