/*++

Copyright (c) 1996-1999  Microsoft Corporation

Module Name:

    rtlprop.c

Abstract:

    Implements the management of properties.

Author:

    Rod Gamache (rodga) 7-Jan-1996

Revision History:

    David Potter (davidp) 12-Mar-1997
        Moved to CLUSRTL.

--*/

#define UNICODE 1
#define _UNICODE 1

#include "clusrtlp.h"
#include "stdio.h"
#include "stdlib.h"
#include "RegistryValueName.h"

#define CLRTL_NULL_STRING L"\0"

//
// Data alignment notes
//
// All data (including embedded pointers) are aligned on 32b boundaries (64b
// platforms where not a consideration when this code was originally
// written). This makes some of the typecasting a bit tricky since the
// embedded pointers are really pointers to pointers. All double star pointers
// (i.e., LPBYTE *) have to use UNALIGNED since it is possible that the
// starting address of the pointer, i.e., the value contained in the variable,
// could end in a 4 (which on 64b. platforms is unaligned). (LPBYTE *) becomes
// (LPBYTE UNALIGNED *) or BYTE * UNALIGNED *.
//
// Consider these statements from below:
//
//    LPWSTR UNALIGNED *      ppszOutValue;
//         ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];
//
// ppszOutValue is an automatic variable so its address is guaranteed to be
// aligned. pOutParams is essentially an array of DWORDS (though we make no
// effort to enforce this. If that changed, property lists would be broken in
// many different places). It is possible that when we take the address of
// pOutParams + Offset, the offset may be on a DWORD boundary and not a QUAD
// boundary. Therefore we have to treat ppszOutValue's value (a pointer to
// WCHAR) as unaligned (the data itself is properly aligned). You can read the
// typecast as "an aligned pointer (ppszOutValue) to an unaligned pointer
// (address of pOutParams[Offset]) to an aligned WCHAR."
//
// LARGE_INTEGER allows us to "cheat" in that we can leverage the internal
// struct definition of LARGER_INTEGER to do 2 DWORD copies instead of
// treating the data for worst-case alignment.
//
// ISSUE-01/03/16 charlwi CLUSPROP_BUFFER_HELPER might not be correctly aligned
//
// still unsure about this but seeing how the whole property thing is
// 32b. aligned and this structure is used copiously for casting (at DWORD
// boundaries), the potential exists to have a pointer that is not QUADWORD
// aligned. When we go to deref it, we get an alignment fault.
//

//
// Static function prototypes.
//

static
DWORD
WINAPI
ClRtlpSetDwordProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_DWORD pInDwordValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );

static
DWORD
WINAPI
ClRtlpSetLongProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_LONG pInLongValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );

static
DWORD
WINAPI
ClRtlpSetULargeIntegerProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_ULARGE_INTEGER pInULargeIntegerValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );

static
DWORD
WINAPI
ClRtlpSetLargeIntegerProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_LARGE_INTEGER pInLargeIntegerValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );

static
DWORD
WINAPI
ClRtlpSetStringProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_SZ pInStringValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );

static
DWORD
WINAPI
ClRtlpSetMultiStringProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_MULTI_SZ pInMultiStringValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );

static
DWORD
WINAPI
ClRtlpSetBinaryProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_BINARY pInBinaryValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    );



DWORD
WINAPI
ClRtlEnumProperties(
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    OUT LPWSTR pszOutProperties,
    IN DWORD cbOutPropertiesSize,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Enumerates the properties for a given object.

Arguments:

    pPropertyTable - Pointer to the property table to process.

    pszOutProperties - Supplies the output buffer.

    cbOutPropertiesSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pszOutProperties.

    pcbRequired - The required number of bytes if pszOutProperties is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    A Win32 error code on failure.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    DWORD                   totalBufferLength = 0;
    PRESUTIL_PROPERTY_ITEM  property;
    LPWSTR                  psz = pszOutProperties;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    if ( pPropertyTable == NULL ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlEnumProperties: pPropertyTable == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Clear the output buffer
    //
    if ( pszOutProperties != NULL ) {
        ZeroMemory( pszOutProperties, cbOutPropertiesSize );
    }

    //
    // Get the size of all property names for this object.
    //
    for ( property = pPropertyTable ; property->Name != NULL ; property++ ) {
        totalBufferLength += (lstrlenW( property->Name ) + 1) * sizeof(WCHAR);
    }

    totalBufferLength += sizeof(UNICODE_NULL);

    //
    // If the output buffer is big enough, copy the property names.
    //
    if ( totalBufferLength > cbOutPropertiesSize ) {
        *pcbRequired = totalBufferLength;
        totalBufferLength = 0;
        if ( pszOutProperties == NULL ) {
            status = ERROR_SUCCESS;
        } else {
            status = ERROR_MORE_DATA;
        }
    } else {
        DWORD   cchCurrentNameSize;
        for ( property = pPropertyTable ; property->Name != NULL ; property++ ) {
            lstrcpyW( psz, property->Name );
            cchCurrentNameSize = lstrlenW( psz ) + 1;
            *pcbBytesReturned += cchCurrentNameSize * sizeof(WCHAR);
            psz += cchCurrentNameSize;
        }

        *psz = L'\0';
        *pcbBytesReturned += sizeof(WCHAR);
    }

    return(status);

} // ClRtlEnumProperties



DWORD
WINAPI
ClRtlEnumPrivateProperties(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    OUT LPWSTR pszOutProperties,
    IN DWORD cbOutPropertiesSize,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Enumerates the properties for a given object.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pszOutProperties - Supplies the output buffer.

    cbOutPropertiesSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pszOutProperties.

    pcbRequired - The required number of bytes if pszOutProperties is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    A Win32 error code on failure.

--*/

{
    DWORD       status = ERROR_SUCCESS;
    DWORD       totalBufferLength = 0;
    LPWSTR      psz = pszOutProperties;
    DWORD       ival;
    DWORD       currentNameLength = 20;
    DWORD       nameLength;
    DWORD       dataLength;
    DWORD       type;
    LPWSTR      pszName;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    //
    // Validate inputs
    //
    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis == NULL) ) {

        ClRtlDbgPrint( LOG_CRITICAL,
                       "ClRtlEnumPrivateProperties: hkeyClusterKey or pClusterRegApis == NULL. "
                       "Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Clear the output buffer
    //
    if ( pszOutProperties != NULL ) {
        ZeroMemory( pszOutProperties, cbOutPropertiesSize );
    }

    //
    // Allocate a property name buffer.
    //
    pszName = static_cast< LPWSTR >( LocalAlloc( LMEM_FIXED, currentNameLength * sizeof(WCHAR) ) );
    if ( pszName == NULL ) {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Enumerate all properties to find the total size.
    //
    ival = 0;
    while ( TRUE ) {
        //
        // Read the next property.
        //
        nameLength = currentNameLength;
        dataLength = 0;
        status = (*pClusterRegApis->pfnEnumValue)( hkeyClusterKey,
                                                   ival,
                                                   pszName,
                                                   &nameLength,
                                                   &type,
                                                   NULL,
                                                   &dataLength );
        if ( status == ERROR_NO_MORE_ITEMS ) {
            status = ERROR_SUCCESS;
            break;
        } else if ( status == ERROR_MORE_DATA ) {

            CL_ASSERT( (nameLength+1) > currentNameLength );

            LocalFree( pszName );

            currentNameLength = nameLength + 1; // returned value doesn't include terminating NULL
            pszName = static_cast< LPWSTR >( LocalAlloc( LMEM_FIXED, currentNameLength * sizeof(WCHAR) ) );
            if ( pszName == NULL ) {
                status = ERROR_NOT_ENOUGH_MEMORY;
                break;
            }

            continue; // retry

        } else if ( status != ERROR_SUCCESS ) {
            break;
        }

        totalBufferLength += (nameLength + 1) * sizeof(WCHAR);
        ++ival;
    }

    //
    // Continue only if the operations so far have been successful.
    //
    if ( status == ERROR_SUCCESS ) {

        if ( totalBufferLength != 0 ) {
            totalBufferLength += sizeof(UNICODE_NULL);
        }

        //
        // If the output buffer is big enough, copy the property names.
        //
        if ( totalBufferLength > cbOutPropertiesSize ) {
            *pcbRequired = totalBufferLength;
            totalBufferLength = 0;
            if ( (pszOutProperties == NULL) ||
                 (cbOutPropertiesSize == 0) ) {
                status = ERROR_SUCCESS;
            } else {
                status = ERROR_MORE_DATA;
            }
        } else if ( totalBufferLength != 0 ) {
            //
            // Enumerate all properties for copying
            //
            for ( ival = 0; ; ival++ ) {
                //
                // Read the next property.
                //
                nameLength = currentNameLength;
                dataLength = 0;
                status = (*pClusterRegApis->pfnEnumValue)( hkeyClusterKey,
                                                           ival,
                                                           pszName,
                                                           &nameLength,
                                                           &type,
                                                           NULL,
                                                           &dataLength );

                if ( status == ERROR_NO_MORE_ITEMS ) {
                    status = ERROR_SUCCESS;
                    break;
                } else if ( status == ERROR_MORE_DATA ) {
                    CL_ASSERT( 0 ); // THIS SHOULDN'T HAPPEN
                } else if ( status != ERROR_SUCCESS ) {
                    break;
                }

                //CL_ASSERT( (DWORD)lstrlenW( name ) == nameLength );
                lstrcpyW( psz, pszName );
                psz += nameLength + 1;
                *pcbBytesReturned += (nameLength + 1) * sizeof(WCHAR);
            }

            *psz = L'\0';
            *pcbBytesReturned += sizeof(WCHAR);
        }
    }

    LocalFree( pszName );

    return(status);

} // ClRtlEnumPrivateProperties



DWORD
WINAPI
ClRtlGetProperties(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    OUT PVOID pOutPropertyList,
    IN DWORD cbOutPropertyListSize,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Gets the properties for a given object.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property list to process.

    pOutPropertyList - Supplies the output buffer.

    cbOutPropertyListSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    A Win32 error code on failure.

--*/

{
    DWORD           status = ERROR_SUCCESS;
    DWORD           itemCount = 0;
    DWORD           totalBufferLength = 0;
    PVOID           outBuffer = pOutPropertyList;
    DWORD           bufferLength = cbOutPropertyListSize;
    PRESUTIL_PROPERTY_ITEM  property;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis == NULL) ||
         (pPropertyTable == NULL) ) {

        ClRtlDbgPrint( LOG_CRITICAL,
                       "ClRtlGetProperties: hkeyClusterKey, pClusterRegApis, or "
                       "pPropertyTable == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Clear the output buffer
    //
    if ( pOutPropertyList != NULL ) {
        ZeroMemory( pOutPropertyList, cbOutPropertyListSize );
    }

    //
    // Get the size of all properties for this object.
    //
    property = pPropertyTable;
    while ( property->Name != NULL ) {
        status = ClRtlGetPropertySize( hkeyClusterKey,
                                       pClusterRegApis,
                                       property,
                                       &totalBufferLength,
                                       &itemCount );

        if ( status != ERROR_SUCCESS ) {
            break;
        }
        property++;
    }


    //
    // Continue only if the operations so far have been successful.
    //
    if ( status == ERROR_SUCCESS ) {
        //
        // Count for item count at front of return data and endmark.
        //
        totalBufferLength += sizeof(DWORD) + sizeof(CLUSPROP_SYNTAX);

        //
        // Verify the size of all the properties
        //
        if ( totalBufferLength > cbOutPropertyListSize ) {
            *pcbRequired = totalBufferLength;
            totalBufferLength = 0;
            if ( pOutPropertyList == NULL ) {
                status = ERROR_SUCCESS;
            } else {
                status = ERROR_MORE_DATA;
            }
        }
        else {
            *(LPDWORD)outBuffer = itemCount;
            outBuffer = (PVOID)( (PUCHAR)outBuffer + sizeof(itemCount) );
            bufferLength -= sizeof(itemCount);

            //
            // Now fetch all of the properties.
            //
            property = pPropertyTable;
            while ( property->Name != NULL ) {
                status = ClRtlGetProperty( hkeyClusterKey,
                                           pClusterRegApis,
                                           property,
                                           &outBuffer,
                                           &bufferLength );

                if ( status != ERROR_SUCCESS ) {
                    break;
                }
                property++;
            }

            // Don't forget the ENDMARK
            *(LPDWORD)outBuffer = CLUSPROP_SYNTAX_ENDMARK;

            if ( (status != ERROR_SUCCESS) &&
                 (status != ERROR_MORE_DATA) ) {
                totalBufferLength = 0;
            }
        }

        *pcbBytesReturned = totalBufferLength;
    }

    return(status);

} // ClRtlGetProperties



DWORD
WINAPI
ClRtlGetAllProperties(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    OUT PVOID pOutPropertyList,
    IN DWORD cbOutPropertyListSize,
    OUT LPDWORD pcbReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Gets the default and 'unknown' properties for a given object.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    pOutPropertyList - Supplies the output buffer.

    cbOutPropertyListSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER      cbhKnownPropBuffer;
    CLUSPROP_BUFFER_HELPER      cbhUnknownPropBuffer;
    DWORD                       cbKnownPropBufferSize;
    DWORD                       cbUnknownPropBufferSize;
    DWORD                       sc = ERROR_SUCCESS;
    DWORD                       scUnknown;
    DWORD                       dwSavedData;
    DWORD                       cbUnknownRequired = 0;
    DWORD                       cbUnknownReturned = 0;


    cbhKnownPropBuffer.pb = static_cast< LPBYTE >( pOutPropertyList );
    cbKnownPropBufferSize = cbOutPropertyListSize;

    //
    // First get the 'known' properties.
    //
    sc = ClRtlGetProperties(
                hkeyClusterKey,
                pClusterRegApis,
                pPropertyTable,
                cbhKnownPropBuffer.pb,
                cbKnownPropBufferSize,
                pcbReturned,
                pcbRequired
                );

    if ( sc != ERROR_SUCCESS )
    {
        *pcbReturned = 0;

        if ( sc != ERROR_MORE_DATA )
        {
            *pcbRequired = 0;
            return sc;
        }

        // We already know that there is insufficient space.
        scUnknown = ClRtlGetUnknownProperties(
                            hkeyClusterKey,
                            pClusterRegApis,
                            pPropertyTable,
                            NULL,
                            0,
                            &cbUnknownReturned,
                            &cbUnknownRequired
                            );

        if ( ( scUnknown != ERROR_SUCCESS ) &&
             ( scUnknown != ERROR_MORE_DATA ) )
        {
            *pcbRequired = 0;
             return scUnknown;
        }

        //
        // If both known and unknown properties exist, only one endmark
        // is required. So, subtract endmark size from total required size.
        //
        if ( ( *pcbRequired > sizeof(DWORD) ) &&
             ( cbUnknownRequired > sizeof(DWORD) ) )
        {
             *pcbRequired -= sizeof(CLUSPROP_SYNTAX);
        }

        //
        // Subtract off the size of the property count for the
        // unknown property list.
        //
        *pcbRequired += cbUnknownRequired - sizeof(DWORD);
        return sc;
    } // if: call to ClRtlGetProperties failed

    // If we are here then the call to ClRtlGetProperties succeeded.

    //
    // Calculate the position in the output buffer where unknown properties
    // should be stored.  Subtract off the size of the property count for
    // the unknown property list.  These calculations will cause the buffer
    // pointer to overlap the known property list buffer by one DWORD.
    //
    cbhUnknownPropBuffer.pb = cbhKnownPropBuffer.pb + *pcbReturned - sizeof(DWORD);
    cbUnknownPropBufferSize = cbKnownPropBufferSize - *pcbReturned + sizeof(DWORD);

    // If there are known properties, move the unknown property list
    // buffer pointer to overlap that as well.
    if ( *pcbReturned > sizeof(DWORD) )
    {
        cbhUnknownPropBuffer.pb -= sizeof(CLUSPROP_SYNTAX);
        cbUnknownPropBufferSize += sizeof(CLUSPROP_SYNTAX);
    } // if: a nonzero number of properties has been returned.

    //
    // Save the DWORD we are about to overlap.
    //
    dwSavedData = *(cbhUnknownPropBuffer.pdw);

    scUnknown = ClRtlGetUnknownProperties(
                        hkeyClusterKey,
                        pClusterRegApis,
                        pPropertyTable,
                        cbhUnknownPropBuffer.pb,
                        cbUnknownPropBufferSize,
                        &cbUnknownReturned,
                        &cbUnknownRequired
                        );

    if ( scUnknown == ERROR_SUCCESS )
    {
        //
        // The order of the next three statements is very important
        // since the known and the unknown property buffers can overlap.
        //
        DWORD nUnknownPropCount = cbhUnknownPropBuffer.pList->nPropertyCount;
        *(cbhUnknownPropBuffer.pdw) = dwSavedData;
        cbhKnownPropBuffer.pList->nPropertyCount += nUnknownPropCount;

        //
        // If both known and unknown properties exist, only one endmark
        // is required. So, subtract endmark size from total returned size.
        //
        if ( ( *pcbReturned > sizeof(DWORD) ) && 
             ( cbUnknownReturned > sizeof(DWORD) ) )
        {
            *pcbReturned -= sizeof(CLUSPROP_SYNTAX);
        }

        //
        // Add in the size of the unknown property list minus the
        // size of the unknown property list property count.
        //
        *pcbReturned += cbUnknownReturned - sizeof(DWORD);
        *pcbRequired = 0;

    } // if: call to ClRtlGetUnknownProperties succeeded
    else
    {
        if ( scUnknown == ERROR_MORE_DATA )
        {
            *pcbRequired = *pcbReturned;
            *pcbReturned = 0;

            //
            // Both known and unknown properties exist. Only one endmark
            // is required. So, subtract endmark size from total required size.
            //
            if ( ( *pcbRequired > sizeof(DWORD) ) &&
                 ( cbUnknownRequired > sizeof(DWORD) ) )
            {
                 *pcbRequired -= sizeof(CLUSPROP_SYNTAX);
            }

            //
            // Add in the size of the unknown property list minus the
            // size of the unknown property list property count.
            //
            *pcbRequired += cbUnknownRequired - sizeof(DWORD);

        } // if: ClRtlGetUnknownProperties returned ERROR_MORE_DATA
        else
        {
            *pcbRequired = 0;
            *pcbReturned = 0;

        } // else: ClRtlGetUnknownProperties failed for some unknown reason.

    } // else: Call to ClRtlGetUnknownProperties failed.

    return scUnknown;

} //*** ClRtlGetAllProperties()


DWORD
WINAPI
ClRtlGetPropertiesToParameterBlock(
    IN HKEY hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    IN OUT LPBYTE pOutParams,
    IN BOOL bCheckForRequiredProperties,
    OUT OPTIONAL LPWSTR * pszNameOfPropInError
    )

/*++

Routine Description:

    Read properties based on a property table.

Arguments:

    hkeyClusterKey - Supplies the cluster key where the properties are stored.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    pOutParams - Parameter block to read into.

    bCheckForRequiredProperties - Boolean value specifying whether missing
        required properties should cause an error.

    pszNameOfPropInError - String pointer in which to return the name of the
        property in error (optional).

Return Value:

    ERROR_SUCCESS - Properties read successfully.

    ERROR_INVALID_DATA - Required property not present.

    ERROR_INVALID_PARAMETER - 

    A Win32 error code on failure.

--*/

{
    PRESUTIL_PROPERTY_ITEM  propertyItem = pPropertyTable;
    HKEY                    key;
    DWORD                   status = ERROR_SUCCESS;
    DWORD                   valueType;
    DWORD                   valueSize;
    LPWSTR                  pszInValue;
    LPBYTE                  pbInValue;
    DWORD                   dwInValue;
    LPWSTR UNALIGNED *      ppszOutValue;
    LPBYTE UNALIGNED *      ppbOutValue;
    DWORD *                 pdwOutValue;
    LONG *                  plOutValue;
    ULARGE_INTEGER *        pullOutValue;
    LARGE_INTEGER *         pllOutValue;
    CRegistryValueName      rvn;


    if ( pszNameOfPropInError != NULL ) {
        *pszNameOfPropInError = NULL;
    }

    if ( (hkeyClusterKey == NULL) ||
         (pPropertyTable == NULL) ||
         (pOutParams == NULL) ||
         (pClusterRegApis == NULL) ||
         (pClusterRegApis->pfnOpenKey == NULL) ||
         (pClusterRegApis->pfnCloseKey == NULL) ||
         (pClusterRegApis->pfnQueryValue == NULL) ) {

        ClRtlDbgPrint( LOG_CRITICAL,
                       "ClRtlGetPropertiesToParameterBlock: hkeyClusterKey, pPropertyTable, "
                       "pOutParams, pClusterRegApis, or required pfns == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    while ( propertyItem->Name != NULL ) {
        // 
        // Use the wrapper class CRegistryValueName to parse value name to see if it 
        // contains a backslash.
        // 
        status = rvn.ScInit( propertyItem->Name, propertyItem->KeyName );
        if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // If the value resides at a different location, open the key.
        //
        if ( rvn.PszKeyName() != NULL ) {

            DWORD disposition;

            status = (*pClusterRegApis->pfnOpenKey)( 
                            hkeyClusterKey,
                            rvn.PszKeyName(),
                            KEY_ALL_ACCESS,
                            (void **) &key
                            );

            // If key could not be opened, then we may need to put the default value
            // for the property item.
            if ( status != ERROR_SUCCESS ) {
                status = ERROR_FILE_NOT_FOUND;
            }

        } else {
            key = hkeyClusterKey;
        }

        switch ( propertyItem->Format ) {
            case CLUSPROP_FORMAT_DWORD:
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset];
                valueSize = sizeof(DWORD);

                // If OpenKey has succeeded.
                if ( status == ERROR_SUCCESS ) {
                    status = (*pClusterRegApis->pfnQueryValue)( key,
                                                                rvn.PszName(),
                                                                &valueType,
                                                                (LPBYTE) pdwOutValue,
                                                                &valueSize );
                }

                if ( status == ERROR_SUCCESS ) {
                    if ( valueType != REG_DWORD ) {

                        ClRtlDbgPrint( LOG_CRITICAL,
                                       "ClRtlGetPropertiesToParameterBlock: Property '%1!ls!' "
                                       "expected to be REG_DWORD (%2!d!), was %3!d!.\n",
                                       propertyItem->Name, REG_DWORD, valueType );
                        status = ERROR_INVALID_PARAMETER;
                    }
                } else if ( (status == ERROR_FILE_NOT_FOUND) &&
                            (!(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ||
                             ! bCheckForRequiredProperties) ) {
                    *pdwOutValue = propertyItem->Default;
                    status = ERROR_SUCCESS;
                }
                break;

            case CLUSPROP_FORMAT_LONG:
                plOutValue = (LONG *) &pOutParams[propertyItem->Offset];
                valueSize = sizeof(LONG);
                // If OpenKey has succeeded.
                if ( status == ERROR_SUCCESS ) {
                    status = (*pClusterRegApis->pfnQueryValue)( key,
                                                                rvn.PszName(),
                                                                &valueType,
                                                                (LPBYTE) plOutValue,
                                                                &valueSize );
                }

                if ( status == ERROR_SUCCESS ) {
                    if ( valueType != REG_DWORD ) {

                        ClRtlDbgPrint( LOG_CRITICAL,
                                       "ClRtlGetPropertiesToParameterBlock: Property '%1!ls!' "
                                       "expected to be REG_DWORD (%2!d!), was %3!d!.\n",
                                       propertyItem->Name, REG_DWORD, valueType );
                        status = ERROR_INVALID_PARAMETER;
                    }
                } else if ( (status == ERROR_FILE_NOT_FOUND) &&
                            (!(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ||
                             !bCheckForRequiredProperties) ) {
                    *plOutValue = propertyItem->Default;
                    status = ERROR_SUCCESS;
                }
                break;

            case CLUSPROP_FORMAT_ULARGE_INTEGER:
                pullOutValue = (ULARGE_INTEGER *) &pOutParams[propertyItem->Offset];
                valueSize = sizeof(ULARGE_INTEGER);

                // If OpenKey has succeeded.
                if ( status == ERROR_SUCCESS ) {
                    status = (*pClusterRegApis->pfnQueryValue)( key,
                                                                rvn.PszName(),
                                                                &valueType,
                                                                (LPBYTE) pullOutValue,
                                                                &valueSize );
                }

                if ( status == ERROR_SUCCESS ) {
                    if ( valueType != REG_QWORD ) {

                        ClRtlDbgPrint( LOG_CRITICAL,
                                       "ClRtlGetPropertiesToParameterBlock: Property '%1!ls!' "
                                       "expected to be REG_QWORD (%2!d!), was %3!d!.\n",
                                       propertyItem->Name, REG_QWORD, valueType );
                        status = ERROR_INVALID_PARAMETER;
                    }
                } else if ( (status == ERROR_FILE_NOT_FOUND) &&
                            (!(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ||
                             ! bCheckForRequiredProperties) ) {
                    pullOutValue->u = propertyItem->ULargeIntData->Default.u;
                    status = ERROR_SUCCESS;
                }
                break;

            case CLUSPROP_FORMAT_LARGE_INTEGER:
                pllOutValue = (LARGE_INTEGER *) &pOutParams[propertyItem->Offset];
                valueSize = sizeof(LARGE_INTEGER);

                // If OpenKey has succeeded.
                if ( status == ERROR_SUCCESS ) {
                    status = (*pClusterRegApis->pfnQueryValue)( key,
                                                                rvn.PszName(),
                                                                &valueType,
                                                                (LPBYTE) pllOutValue,
                                                                &valueSize );
                }

                if ( status == ERROR_SUCCESS ) {
                    if ( valueType != REG_QWORD ) {

                        ClRtlDbgPrint( LOG_CRITICAL,
                                       "ClRtlGetPropertiesToParameterBlock: Property '%1!ls!' "
                                       "expected to be REG_QWORD (%2!d!), was %3!d!.\n",
                                       propertyItem->Name, REG_QWORD, valueType );
                        status = ERROR_INVALID_PARAMETER;
                    }
                } else if ( (status == ERROR_FILE_NOT_FOUND) &&
                            (!(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ||
                             ! bCheckForRequiredProperties) )
                {
                    pllOutValue->u = propertyItem->LargeIntData->Default.u;
                    status = ERROR_SUCCESS;
                }
                break;

            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];
                // If OpenKey has succeeded.
                if ( status == ERROR_SUCCESS ) {
                    pszInValue = ClRtlGetSzValue( key,
                                                  rvn.PszName(),
                                                  pClusterRegApis );
                }
                else {
                    pszInValue = NULL;
                    SetLastError(status);
                }

                if ( pszInValue == NULL ) {
                    status = GetLastError();
                    if ( (status == ERROR_FILE_NOT_FOUND) &&
                         (!(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ||
                          ! bCheckForRequiredProperties) ) {

                        status = ERROR_SUCCESS;

                        // Deallocate old value.
                        if ( *ppszOutValue != NULL ) {
                            LocalFree( *ppszOutValue );
                            *ppszOutValue = NULL;
                        }

                        // If a default is specified, copy it.
                        if ( propertyItem->lpDefault != NULL ) {
                            *ppszOutValue = (LPWSTR) LocalAlloc( LMEM_FIXED, (lstrlenW( (LPCWSTR) propertyItem->lpDefault ) + 1) * sizeof(WCHAR) );
                            if ( *ppszOutValue == NULL ) {
                                status = GetLastError();
                            } else {
                                lstrcpyW( *ppszOutValue, (LPCWSTR) propertyItem->lpDefault );
                            }
                        }
                    }
                } else {
                    if ( *ppszOutValue != NULL ) {
                        LocalFree( *ppszOutValue );
                    }
                    *ppszOutValue = pszInValue;
                }
                break;

            case CLUSPROP_FORMAT_MULTI_SZ:
            case CLUSPROP_FORMAT_BINARY:
                ppbOutValue = (LPBYTE UNALIGNED *) &pOutParams[propertyItem->Offset];
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset+sizeof(LPBYTE*)];
                // If OpenKey has succeeded.
                if ( status == ERROR_SUCCESS ) {
                    status = ClRtlGetBinaryValue( key,
                                                  rvn.PszName(),
                                                  &pbInValue,
                                                  &dwInValue,
                                                  pClusterRegApis );
                }

                if ( status == ERROR_SUCCESS ) {
                    if ( *ppbOutValue != NULL ) {
                        LocalFree( *ppbOutValue );
                    }
                    *ppbOutValue = pbInValue;
                    *pdwOutValue = dwInValue;
                } else if ( (status == ERROR_FILE_NOT_FOUND) &&
                            (!(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ||
                             ! bCheckForRequiredProperties) ) {

                    status = ERROR_SUCCESS;

                    // Deallocate old value.
                    if ( *ppbOutValue != NULL ) {
                        LocalFree( *ppbOutValue );
                        *ppbOutValue = NULL;
                    }

                    *pdwOutValue = 0;

                    // If a default is specified, copy it.
                    if ( propertyItem->lpDefault != NULL ) {
                        *ppbOutValue = (LPBYTE) LocalAlloc( LMEM_FIXED, propertyItem->Minimum );
                        if ( *ppbOutValue == NULL ) {
                            status = GetLastError();
                        } else {
                            CopyMemory( *ppbOutValue, (const PVOID) propertyItem->lpDefault, propertyItem->Minimum );
                            *pdwOutValue = propertyItem->Minimum;
                        }
                    }
                }
                break;
        }

        //
        // Close the key if we opened it.
        //
        if ( (rvn.PszKeyName() != NULL) &&
             (key != NULL) ) {
            (*pClusterRegApis->pfnCloseKey)( key );
        }

        //
        // Handle any errors that occurred.
        //
        if ( status != ERROR_SUCCESS ) {
            if ( pszNameOfPropInError != NULL ) {
                *pszNameOfPropInError = propertyItem->Name;
            }
            if ( propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED ) {
                if ( status == ERROR_FILE_NOT_FOUND ) {
                    status = ERROR_INVALID_DATA;
                }
                break;
            } else {
                status = ERROR_SUCCESS;
            }
        }

        //
        // Advance to the next property.
        //
        propertyItem++;
    }

    return(status);

} // ClRtlGetPropertiesToParameterBlock



DWORD
WINAPI
ClRtlPropertyListFromParameterBlock(
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    OUT PVOID  pOutPropertyList,
    IN OUT LPDWORD pcbOutPropertyListSize,
    IN const LPBYTE pInParams,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Constructs a property list from a parameter block.

Arguments:

    pPropertyTable - Pointer to the property table to process.

    pOutPropertyList - Supplies the output buffer.

    pcbOutPropertyListSize - Supplies the size of the output buffer.

    pInParams - Supplies the input parameter block.

    pcbBytesReturned - The number of bytes returned in pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    ERROR_MORE_DATA - Output buffer isn't big enough to build the property list.

    A Win32 error code on failure.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    DWORD                   bytesReturned;
    DWORD                   required;
    DWORD                   nameSize;
    DWORD                   dataSize;
    DWORD                   bufferIncrement;
    DWORD                   totalBufferSize = 0;
    LPDWORD                 ptrItemCount;
    WORD                    propertyFormat;
    BOOL                    copying = TRUE;
    PRESUTIL_PROPERTY_ITEM  propertyItem = pPropertyTable;
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR UNALIGNED *      ppszValue;
    PBYTE UNALIGNED *       ppbValue;
    DWORD *                 pdwValue;
    ULARGE_INTEGER *        pullValue;
    LPWSTR                  pszUnexpanded;
    LPWSTR                  pszExpanded = NULL;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    if ( (pPropertyTable == NULL) ||
         (pInParams == NULL) ||
         (pcbOutPropertyListSize == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlPropertyListFromParameterBlock: pPropertyTable, pInParams, or pcbOutPropertyListSize == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Clear the output buffer.
    //
    if ( pOutPropertyList != NULL ) {
        ZeroMemory( pOutPropertyList, *pcbOutPropertyListSize );
    } else {
        copying = FALSE;
    }

    //
    // Need a DWORD of item count.
    //
    props.pb = (LPBYTE) pOutPropertyList;
    ptrItemCount = props.pdw++;

    totalBufferSize += sizeof(DWORD);
    if ( totalBufferSize > *pcbOutPropertyListSize ) {
        copying = FALSE;
    }

    while ( propertyItem->Name != NULL ) {
        //
        // Copy the property name.
        //
        nameSize = (lstrlenW( propertyItem->Name ) + 1) * sizeof(WCHAR);
        bufferIncrement = sizeof(CLUSPROP_PROPERTY_NAME) + ALIGN_CLUSPROP( nameSize );
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
            props.pName->Syntax.dw = CLUSPROP_SYNTAX_NAME;
            props.pName->cbLength = nameSize;
            lstrcpyW( props.pName->sz, propertyItem->Name );
            props.pb += bufferIncrement;
        } else {
            copying = FALSE;
        }

        //
        // Copy the property value.
        //
        propertyFormat = (WORD) propertyItem->Format;
        switch ( propertyItem->Format ) {

            case CLUSPROP_FORMAT_DWORD:
            case CLUSPROP_FORMAT_LONG:
                pdwValue = (DWORD *) &pInParams[propertyItem->Offset];
                bufferIncrement = sizeof(CLUSPROP_DWORD);
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = propertyFormat;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pDwordValue->cbLength = sizeof(DWORD);
                    props.pDwordValue->dw = *pdwValue;
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            case CLUSPROP_FORMAT_ULARGE_INTEGER:
            case CLUSPROP_FORMAT_LARGE_INTEGER:
                pullValue = (ULARGE_INTEGER *) &pInParams[propertyItem->Offset];
                bufferIncrement = sizeof(CLUSPROP_ULARGE_INTEGER);
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = propertyFormat;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pULargeIntegerValue->cbLength = sizeof(ULARGE_INTEGER);
                    props.pULargeIntegerValue->li.u = pullValue->u;
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                ppszValue = (LPWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                pszUnexpanded = *ppszValue;
                if ( *ppszValue != NULL ) {
                    dataSize = (lstrlenW( *ppszValue ) + 1) * sizeof(WCHAR);
                } else {
                    dataSize = sizeof(WCHAR);
                }
                bufferIncrement = sizeof(CLUSPROP_SZ) + ALIGN_CLUSPROP( dataSize );
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = propertyFormat;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pStringValue->cbLength = dataSize;
                    if ( *ppszValue != NULL ) {
                        lstrcpyW( props.pStringValue->sz, *ppszValue );
                    } else {
                        props.pStringValue->sz[0] = L'\0';
                    }
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }

                //
                // See if there is a different expanded string and, if so,
                // return it as an additional value in the value list.
                //
                if ( pszUnexpanded != NULL ) {
                    pszExpanded = ClRtlExpandEnvironmentStrings( pszUnexpanded );
                    if ( pszExpanded == NULL ) {
                        status = GetLastError();
                        break;
                    }
                    if ( lstrcmpW( pszExpanded, pszUnexpanded ) != 0 ) {
                        dataSize = (lstrlenW( pszExpanded ) + 1) * sizeof( WCHAR );
                        bufferIncrement = sizeof( CLUSPROP_SZ ) + ALIGN_CLUSPROP( dataSize );
                        totalBufferSize += bufferIncrement;
                        if ( totalBufferSize <= *pcbOutPropertyListSize ) {
                            props.pSyntax->dw = CLUSPROP_SYNTAX_LIST_VALUE_EXPANDED_SZ;
                            props.pStringValue->cbLength = dataSize;
                            lstrcpyW( props.pStringValue->sz, pszExpanded );
                            props.pb += bufferIncrement;
                        } else {
                            copying = FALSE;
                        }
                    }
                    LocalFree( pszExpanded );
                    pszExpanded = NULL;
                }
                break;

            case CLUSPROP_FORMAT_MULTI_SZ:
                ppszValue = (LPWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                pdwValue = (DWORD *) &pInParams[propertyItem->Offset+sizeof(LPWSTR*)];
                if ( *ppszValue != NULL ) {
                    bufferIncrement = sizeof(CLUSPROP_SZ) + ALIGN_CLUSPROP( *pdwValue );
                } else {
                    bufferIncrement = sizeof(CLUSPROP_SZ) + ALIGN_CLUSPROP( sizeof(WCHAR) );
                }
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = propertyFormat;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    if ( *ppszValue != NULL ) {
                        props.pStringValue->cbLength = *pdwValue;
                        CopyMemory( props.pStringValue->sz, *ppszValue, *pdwValue );
                    } else {
                        props.pStringValue->cbLength = sizeof(WCHAR);
                        props.pStringValue->sz[0] = L'\0';
                    }
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            case CLUSPROP_FORMAT_BINARY:
                ppbValue = (PBYTE UNALIGNED *) &pInParams[propertyItem->Offset];
                pdwValue = (DWORD *) &pInParams[propertyItem->Offset+sizeof(PBYTE*)];
                if ( *ppbValue != NULL ) {
                    bufferIncrement = sizeof(CLUSPROP_BINARY) + ALIGN_CLUSPROP( *pdwValue );
                } else {
                    bufferIncrement = sizeof(CLUSPROP_BINARY);
                }
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = propertyFormat;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    if ( *ppbValue != NULL ) {
                        props.pBinaryValue->cbLength = *pdwValue;
                        CopyMemory( props.pBinaryValue->rgb, *ppbValue, *pdwValue );
                    } else {
                        props.pBinaryValue->cbLength = 0;
                    }
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            default:
                break;
        }

        if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // Add the value-closing endmark.
        //
        bufferIncrement = sizeof(CLUSPROP_SYNTAX);
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
            props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
            props.pb += bufferIncrement;
            (*ptrItemCount)++;
        } else {
            copying = FALSE;
        }

        //
        // Advance to the next property.
        //
        propertyItem++;
    }

    if ( status == ERROR_SUCCESS ) {
        // Don't forget the ENDMARK.
        totalBufferSize += sizeof(CLUSPROP_SYNTAX);
        if ( copying && (totalBufferSize <= *pcbOutPropertyListSize) ) {
            props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
        } else {
            copying = FALSE;
        }

        //
        // Return size of data.
        //
        if ( copying == FALSE ) {
            *pcbRequired = totalBufferSize;
            *pcbBytesReturned = 0;
            status = ERROR_MORE_DATA;
        } else {
            *pcbRequired = 0;
            *pcbBytesReturned = totalBufferSize;
            status = ERROR_SUCCESS;
        }
    }

    return(status);

} // ClRtlPropertyListFromParameterBlock



DWORD
WINAPI
ClRtlGetPrivateProperties(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    OUT PVOID pOutPropertyList,
    IN DWORD cbOutPropertyListSize,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Gets the private properties for a given object.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pOutPropertyList - Supplies the output buffer.

    cbOutPropertyListSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    ERROR_MORE_DATA - Output buffer isn't big enough to build the property list.

    A Win32 error code on failure.

--*/

{
    DWORD       status = ERROR_SUCCESS;
    DWORD       ival;
    DWORD       currentNameLength = 80;
    DWORD       currentDataLength = 80;
    DWORD       nameLength;
    DWORD       dataLength;
    DWORD       dataLengthExpanded;
    DWORD       type;
    LPWSTR      name;
    PUCHAR      data;
    LPDWORD     ptrItemCount;
    DWORD       itemCount = 0;
    BOOL        copying = TRUE;
    DWORD       totalBufferSize = 0;
    DWORD       bufferIncrement;
    DWORD       bufferIncrementExpanded;
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR      pszExpanded = NULL;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis->pfnEnumValue == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetPrivateProperties: hkeyClusterKey or pClusterRegApis->pfnEnumValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Clear the output buffer
    //
    if ( pOutPropertyList != NULL ) {
        ZeroMemory( pOutPropertyList, cbOutPropertyListSize );
    } else {
        copying = FALSE;
    }

    //
    // Need a DWORD of item count.
    //
    props.pb = (LPBYTE) pOutPropertyList;
    ptrItemCount = props.pdw++;

    totalBufferSize += sizeof(DWORD);
    if ( totalBufferSize > cbOutPropertyListSize ) {
        copying = FALSE;
    }

    //
    // Allocate a property name buffer.
    //
    name = (LPWSTR) LocalAlloc( LMEM_FIXED, currentNameLength * sizeof(WCHAR) );
    if ( name == NULL ) {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Allocate a property value data buffer.
    //
    data = (PUCHAR) LocalAlloc( LMEM_FIXED, currentDataLength );
    if ( data == NULL ) {
        LocalFree( name );
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Enumerate all properties and return them!
    //
    for ( ival = 0; ; ival++ ) {
retry:
        //
        // Read the next property.
        //
        nameLength = currentNameLength;
        dataLength = currentDataLength;
        status = (*pClusterRegApis->pfnEnumValue)( hkeyClusterKey,
                                                   ival,
                                                   name,
                                                   &nameLength,
                                                   &type,
                                                   data,
                                                   &dataLength );

        if ( status == ERROR_MORE_DATA ) {
            if ( (nameLength+1) > currentNameLength ) {
                currentNameLength = nameLength+1;
                LocalFree( name );
                name = (LPWSTR) LocalAlloc( LMEM_FIXED, currentNameLength * sizeof(WCHAR) );
                if ( name == NULL ) {
                    status = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }
            }
            if ( dataLength > currentDataLength ) {
                currentDataLength = dataLength;
                LocalFree( data );
                data = (PUCHAR) LocalAlloc( LMEM_FIXED, currentDataLength );
                if ( data == NULL ) {
                    status = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }
            }
            goto retry;
        } else if ( status == ERROR_NO_MORE_ITEMS ) {
            status = ERROR_SUCCESS;
            break;
        } else if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // Skip this property if it isn't of a known type.
        //
        if ( (type != REG_SZ) &&
             (type != REG_EXPAND_SZ) &&
             (type != REG_MULTI_SZ) &&
             (type != REG_BINARY) &&
             (type != REG_DWORD) &&
             (type != REG_QWORD) ) {
            continue;
        }

        itemCount++;

        //
        // Copy the property name.
        // Need a DWORD for the next name Syntax + DWORD for name byte count +
        // the namelength (in bytes? + NULL?)... must be rounded!
        //
        bufferIncrement = sizeof(CLUSPROP_PROPERTY_NAME)
                            + ALIGN_CLUSPROP( (nameLength + 1) * sizeof(WCHAR) );
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
            props.pName->Syntax.dw = CLUSPROP_SYNTAX_NAME;
            props.pName->cbLength = (nameLength + 1) * sizeof(WCHAR);
            lstrcpyW( props.pName->sz, name);
            props.pb += bufferIncrement;
        } else {
            copying = FALSE;
        }

        switch ( type ) {

            case REG_SZ:
            case REG_EXPAND_SZ:
            case REG_MULTI_SZ:
            case REG_BINARY:
                bufferIncrement = sizeof(CLUSPROP_BINARY)
                                    + ALIGN_CLUSPROP( dataLength );
                totalBufferSize += bufferIncrement;
                if ( ( type == REG_SZ ) 
                  || ( type == REG_EXPAND_SZ ) ) {
                    pszExpanded = ClRtlExpandEnvironmentStrings( (LPCWSTR) data );
                    if ( pszExpanded == NULL ) {
                        status = GetLastError();
                        break;
                    }
                    if ( lstrcmpW( pszExpanded, (LPCWSTR) data ) != 0 ) {
                        dataLengthExpanded = (lstrlenW( pszExpanded ) + 1) * sizeof( WCHAR );
                        bufferIncrementExpanded = sizeof( CLUSPROP_SZ ) + ALIGN_CLUSPROP( dataLengthExpanded );
                        totalBufferSize += bufferIncrementExpanded;
                    }
                }
                if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
                    if ( type == REG_SZ ) {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_SZ;
                    } else if ( type == REG_EXPAND_SZ ) {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_EXPAND_SZ;
                    } else if ( type == REG_MULTI_SZ ) {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_MULTI_SZ;
                    } else {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_BINARY;
                    }
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pBinaryValue->cbLength = dataLength;
                    CopyMemory( props.pBinaryValue->rgb, data, dataLength );
                    props.pb += bufferIncrement;

                    //
                    // For SZ or EXPAND_SZ, see if there is a different
                    // expanded string and, if so, return it as an additional
                    // value in the value list.
                    //
                    if (    ( type == REG_SZ )
                        ||  ( type == REG_EXPAND_SZ ) ) {
                        if ( lstrcmpW( pszExpanded, (LPCWSTR) data ) != 0 ) {
                            props.pSyntax->dw = CLUSPROP_SYNTAX_LIST_VALUE_EXPANDED_SZ;
                            props.pStringValue->cbLength = dataLengthExpanded;
                            lstrcpyW( props.pStringValue->sz, pszExpanded );
                            props.pb += bufferIncrementExpanded;
                        }
                    }
                } else {
                    copying = FALSE;
                }
                if ( ( ( type == REG_SZ ) || ( type == REG_EXPAND_SZ ) )
                  && ( pszExpanded != NULL ) )
                {
                    LocalFree( pszExpanded );
                    pszExpanded = NULL;
                }
                break;

            case REG_DWORD:
                bufferIncrement = sizeof(CLUSPROP_DWORD);
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = CLUSPROP_FORMAT_DWORD;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pDwordValue->cbLength = sizeof(DWORD);
                    props.pDwordValue->dw = *(LPDWORD)data;
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            case REG_QWORD:
                bufferIncrement = sizeof(CLUSPROP_ULARGE_INTEGER);
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = CLUSPROP_FORMAT_ULARGE_INTEGER;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pULargeIntegerValue->cbLength = sizeof(ULARGE_INTEGER);
                    props.pULargeIntegerValue->li.u = ((ULARGE_INTEGER *)data)->u;
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            default:
                break;
        }

        if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // Add the closing endmark.
        //
        bufferIncrement = sizeof(CLUSPROP_SYNTAX);
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
            props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
            props.pb += bufferIncrement;
        } else {
            copying = FALSE;
        }

    }

    if ( status == ERROR_SUCCESS ) {
        //
        // Add the closing endmark.
        //
        bufferIncrement = sizeof(CLUSPROP_SYNTAX);
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
            props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
            props.pb += bufferIncrement;
        } else {
            copying = FALSE;
        }
    }

    LocalFree( name );
    LocalFree( data );

    if ( status == ERROR_SUCCESS ) {
        if ( !copying ) {
            *pcbRequired = totalBufferSize;
            status = ERROR_MORE_DATA;
        } else {
            *ptrItemCount = itemCount;
            *pcbBytesReturned = totalBufferSize;
            status = ERROR_SUCCESS;
        }
    }

    return(status);

} // ClRtlGetPrivateProperties



DWORD
WINAPI
ClRtlGetUnknownProperties(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    OUT PVOID pOutPropertyList,
    IN DWORD cbOutPropertyListSize,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Gets the unknown properties for a given object.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    pOutPropertyList - Supplies the output buffer.

    cbOutPropertyListSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    ERROR_MORE_DATA - Output buffer isn't big enough to build the property list.

    A Win32 error code on failure.

--*/

{
    DWORD       status = ERROR_SUCCESS;
    DWORD       ival;
    DWORD       currentNameLength = 80;
    DWORD       currentDataLength = 80;
    DWORD       nameLength;
    DWORD       dataLength;
    DWORD       dataLengthExpanded;
    DWORD       type;
    LPWSTR      name;
    PUCHAR      data;
    LPDWORD     ptrItemCount;
    DWORD       itemCount = 0;
    BOOL        copying = TRUE;
    DWORD       totalBufferSize = 0;
    DWORD       bufferIncrement;
    DWORD       bufferIncrementExpanded;
    BOOL        found;
    LPWSTR      pszExpanded = NULL;

    CLUSPROP_BUFFER_HELPER  props;
    PRESUTIL_PROPERTY_ITEM  property;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis->pfnEnumValue == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetPrivateProperties: hkeyClusterKey or pClusterRegApis->pfnEnumValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Clear the output buffer
    //
    if ( pOutPropertyList != NULL ) {
        ZeroMemory( pOutPropertyList, cbOutPropertyListSize );
    } else {
        copying = FALSE;
    }

    //
    // Need a DWORD of item count.
    //
    props.pb = (LPBYTE) pOutPropertyList;
    ptrItemCount = props.pdw++;

    totalBufferSize += sizeof(DWORD);
    if ( totalBufferSize > cbOutPropertyListSize ) {
        copying = FALSE;
    }

    //
    // Allocate a property name buffer.
    //
    name = (LPWSTR) LocalAlloc( LMEM_FIXED, currentNameLength * sizeof(WCHAR) );
    if ( name == NULL ) {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Allocate a property value data buffer.
    //
    data = (PUCHAR) LocalAlloc( LMEM_FIXED, currentDataLength );
    if ( data == NULL ) {
        LocalFree( name );
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Enumerate all properties and return them!
    //
    for ( ival = 0; ; ival++ ) {
retry:
        //
        // Read the next property.
        //
        nameLength = currentNameLength;
        dataLength = currentDataLength;
        status = (*pClusterRegApis->pfnEnumValue)( hkeyClusterKey,
                                                   ival,
                                                   name,
                                                   &nameLength,
                                                   &type,
                                                   data,
                                                   &dataLength );

        if ( status == ERROR_MORE_DATA ) {
            if ( (nameLength+1) > currentNameLength ) {
                currentNameLength = nameLength+1;
                LocalFree( name );
                name = (LPWSTR) LocalAlloc( LMEM_FIXED, currentNameLength * sizeof(WCHAR) );
                if ( name == NULL ) {
                    status = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }
            }
            if ( dataLength > currentDataLength ) {
                currentDataLength = dataLength;
                LocalFree( data );
                data = (PUCHAR) LocalAlloc( LMEM_FIXED, currentDataLength );
                if ( data == NULL ) {
                    status = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }
            }
            goto retry;
        } else if ( status == ERROR_NO_MORE_ITEMS ) {
            status = ERROR_SUCCESS;
            break;
        } else if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // Skip this property if it isn't of a known type.
        //
        if ( (type != REG_SZ) &&
             (type != REG_EXPAND_SZ) &&
             (type != REG_MULTI_SZ) &&
             (type != REG_BINARY) &&
             (type != REG_DWORD) &&
             (type != REG_QWORD) ) {
            continue;
        }

        //
        // Check if this property item is 'known'. If so, continue.
        //
        found = FALSE;
        property = pPropertyTable;
        while ( property->Name != NULL ) {
            if ( lstrcmpiW( property->Name, name ) == 0 ) {
                found = TRUE;
                break;
            }
            property++;
        }
        if ( found ) {
            continue;
        }

        itemCount++;

        //
        // Copy the property name.
        // Need a DWORD for the next name Syntax + DWORD for name byte count +
        // the namelength (in bytes? + NULL?)... must be rounded!
        //
        bufferIncrement = sizeof(CLUSPROP_PROPERTY_NAME)
                            + ALIGN_CLUSPROP( (nameLength + 1) * sizeof(WCHAR) );
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
            props.pName->Syntax.dw = CLUSPROP_SYNTAX_NAME;
            props.pName->cbLength = (nameLength + 1) * sizeof(WCHAR);
            lstrcpyW( props.pName->sz, name);
            props.pb += bufferIncrement;
        } else {
            copying = FALSE;
        }

        switch ( type ) {

            case REG_SZ:
            case REG_EXPAND_SZ:
            case REG_MULTI_SZ:
            case REG_BINARY:
                bufferIncrement = sizeof(CLUSPROP_BINARY)
                                    + ALIGN_CLUSPROP( dataLength );
                totalBufferSize += bufferIncrement;
                if ( type == REG_EXPAND_SZ ) {
                    pszExpanded = ClRtlExpandEnvironmentStrings( (LPCWSTR) data );
                    if ( pszExpanded == NULL ) {
                        status = GetLastError();
                        break;
                    }
                    if ( lstrcmpW( pszExpanded, (LPCWSTR) data ) != 0 ) {
                        dataLengthExpanded = (lstrlenW( pszExpanded ) + 1) * sizeof( WCHAR );
                        bufferIncrementExpanded = sizeof( CLUSPROP_SZ ) + ALIGN_CLUSPROP( dataLengthExpanded );
                        totalBufferSize += bufferIncrementExpanded;
                    }
                }
                if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
                    if ( type == REG_SZ ) {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_SZ;
                    } else if ( type == REG_EXPAND_SZ ) {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_EXPAND_SZ;
                    } else if ( type == REG_MULTI_SZ ) {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_MULTI_SZ;
                    } else {
                        props.pSyntax->wFormat = CLUSPROP_FORMAT_BINARY;
                    }
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pBinaryValue->cbLength = dataLength;
                    CopyMemory( props.pBinaryValue->rgb, data, dataLength );
                    props.pb += bufferIncrement;

                    //
                    // For SZ or EXPAND_SZ, see if there is a different
                    // expanded string and, if so, return it as an additional
                    // value in the value list.
                    //
                    if (    ( type == REG_SZ )
                        ||  ( type == REG_EXPAND_SZ ) )
                    {
                        if ( pszExpanded != NULL ) {
                            if ( lstrcmpW( pszExpanded, (LPCWSTR) data ) != 0 ) {
                                props.pSyntax->dw = CLUSPROP_SYNTAX_LIST_VALUE_EXPANDED_SZ;
                                props.pStringValue->cbLength = dataLengthExpanded;
                                lstrcpyW( props.pStringValue->sz, pszExpanded );
                                props.pb += bufferIncrementExpanded;
                            }
                        }
                    }
                } else {
                    copying = FALSE;
                }
                if ( type == REG_EXPAND_SZ ) {
                    LocalFree( pszExpanded );
                    pszExpanded = NULL;
                }
                break;

            case REG_DWORD:
                bufferIncrement = sizeof(CLUSPROP_DWORD);
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = CLUSPROP_FORMAT_DWORD;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pDwordValue->cbLength = sizeof(DWORD);
                    props.pDwordValue->dw = *(LPDWORD)data;
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            case REG_QWORD:
                bufferIncrement = sizeof(CLUSPROP_ULARGE_INTEGER);
                totalBufferSize += bufferIncrement;
                if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
                    props.pSyntax->wFormat = CLUSPROP_FORMAT_ULARGE_INTEGER;
                    props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;
                    props.pULargeIntegerValue->cbLength = sizeof(ULARGE_INTEGER);
                    props.pULargeIntegerValue->li.u = ((ULARGE_INTEGER *)data)->u;
                    props.pb += bufferIncrement;
                } else {
                    copying = FALSE;
                }
                break;

            default:
                break;
        }

        //
        // Add the closing endmark.
        //
        bufferIncrement = sizeof(CLUSPROP_SYNTAX);
        totalBufferSize += bufferIncrement;
        if ( copying && (totalBufferSize <= cbOutPropertyListSize) ) {
            props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
            props.pb += bufferIncrement;
        } else {
            copying = FALSE;
        }

    }

    LocalFree( name );
    LocalFree( data );

    if ( !copying ) {
        *pcbRequired = totalBufferSize;
        status = ERROR_MORE_DATA;
    } else {
        *ptrItemCount = itemCount;
        *pcbBytesReturned = totalBufferSize;
        status = ERROR_SUCCESS;
    }

    return(status);

} // ClRtlGetUnknownProperties



DWORD
WINAPI
ClRtlAddUnknownProperties(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    IN OUT PVOID pOutPropertyList,
    IN DWORD cbOutPropertyListSize,
    IN OUT LPDWORD pcbBytesReturned,
    IN OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Adds the unknown properties for a given object to the end of a property
    list.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    pOutPropertyList - Supplies the output buffer.

    cbOutPropertyListSize - Supplies the size of the output buffer.

    pcbBytesReturned - On input, contains the number of bytes in use in the
        output buffer.  On output, contains the total number of bytes in
        pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    A Win32 error code on failure.

--*/

{
    DWORD                   status;
    CLUSPROP_BUFFER_HELPER  copyBuffer;
    CLUSPROP_BUFFER_HELPER  outBuffer;
    DWORD                   bufferLength;
    DWORD                   bytesReturned;
    DWORD                   required;

    //
    // Allocate a buffer for getting 'unknown' properties.
    //
    if ( (cbOutPropertyListSize > *pcbBytesReturned) &&
         (*pcbRequired == 0) )
    {
        bufferLength = cbOutPropertyListSize + (2 * sizeof(DWORD)) - *pcbBytesReturned;
        outBuffer.pb = (LPBYTE) LocalAlloc( LMEM_FIXED, bufferLength );
        if ( outBuffer.pb == NULL ) {
            *pcbBytesReturned = 0;
            *pcbRequired = 0;
            return(ERROR_NOT_ENOUGH_MEMORY);
        }
    } else {
        bufferLength = 0;
        outBuffer.pb = NULL;
    }

    //
    // Get the 'unknown' properties.
    //
    status = ClRtlGetUnknownProperties( hkeyClusterKey,
                                        pClusterRegApis,
                                        pPropertyTable,
                                        outBuffer.pb,
                                        bufferLength,
                                        &bytesReturned,
                                        &required );
    if ( status == ERROR_SUCCESS ) {
        //
        // Copy properties if any were found.
        //
        if ( bytesReturned > sizeof(DWORD) ) {
            //
            // Copy the unknown property data to the end of the property list.
            //
            CL_ASSERT( bytesReturned <= bufferLength );
            copyBuffer.pb = (LPBYTE) pOutPropertyList;
            copyBuffer.pList->nPropertyCount += outBuffer.pList->nPropertyCount;
            copyBuffer.pb += *pcbBytesReturned - sizeof(CLUSPROP_SYNTAX);
            CopyMemory( copyBuffer.pb, outBuffer.pb + sizeof(DWORD), bytesReturned - sizeof(DWORD) );
            *pcbBytesReturned += bytesReturned - sizeof(DWORD) - sizeof(CLUSPROP_SYNTAX);
        }
    } else if ( ( status == ERROR_MORE_DATA )
            &&  ( required == sizeof(DWORD) ) ) {
        required = 0;
        status = ERROR_SUCCESS;
    } else {
        if ( *pcbRequired == 0 ) {
            *pcbRequired = *pcbBytesReturned;
        }
        *pcbBytesReturned = 0;
    }

    //
    // If there are any properties, the number of bytes required will include
    // both a property count (DWORD) and an endmark (CLUSPROP_SYNTAX).
    // Subtract these off because these appear in both lists.
    //
    if ( required > sizeof(DWORD) + sizeof(CLUSPROP_SYNTAX) ) {
        required -= sizeof(DWORD) + sizeof(CLUSPROP_SYNTAX);
    }

    //
    // Free the out buffer (which may be NULL)
    //
    LocalFree( outBuffer.pb );

    //
    // Adjust lengths
    //
    *pcbRequired += required;

    return(status);

} // ClRtlAddUnknownProperties



DWORD
WINAPI
ClRtlGetPropertySize(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTableItem,
    IN OUT LPDWORD pcbOutPropertyListSize,
    IN OUT LPDWORD pnPropertyCount
    )

/*++

Routine Description:

    Get the total number of bytes required for this property.

Arguments:

    hkeyClusterKey - Supplies the handle to the key in the cluster database
        to read from.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.


    pPropertyTableItem - Supplies the property table item for the property
        whose size is to be returned.

    pcbOutPropertyListSize - Supplies the size of the output buffer
        required to add this property to a property list.

    pnPropertyCount - The count of properties is incremented.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_INVALID_PARAMETER - 

    A Win32 error code on failure.

--*/

{
    DWORD   status = ERROR_SUCCESS;
    DWORD   valueType;
    DWORD   bytesReturned;
    DWORD   headerLength;
    PVOID   key;
    LPWSTR  pszValue = NULL;
    LPWSTR  pszExpanded = NULL;
    CRegistryValueName rvn;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis->pfnQueryValue == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetPropertySize: hkeyClusterKey or pClusterRegApis->pfnQueryValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    // 
    // Use the wrapper class CRegistryValueName to parse value name to see if it 
    // contains a backslash.
    // 
    status = rvn.ScInit( pPropertyTableItem->Name, pPropertyTableItem->KeyName );
    if ( status != ERROR_SUCCESS )
    {
       return status;
    }

    //
    // If the value resides at a different location, open the key.
    //
    if ( rvn.PszKeyName() != NULL ) {
        if ( (pClusterRegApis->pfnOpenKey == NULL) ||
             (pClusterRegApis->pfnCloseKey == NULL) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetPropertySize: pClusterRegApis->pfnOpenValue or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
            return(ERROR_BAD_ARGUMENTS);
        }

        status = (*pClusterRegApis->pfnOpenKey)( hkeyClusterKey,
                                                 rvn.PszKeyName(),
                                                 KEY_READ,
                                                 &key);
        
    } else {
        key = hkeyClusterKey;
    }

    //
    // Read the value size.
    //
    if ( status == ERROR_SUCCESS ) {
        status = (*pClusterRegApis->pfnQueryValue)( key,
                                                    rvn.PszName(),
                                                    &valueType,
                                                    NULL,
                                                    &bytesReturned );
    }

    //
    // If the value is not present, return the default value.
    //
    if ( status == ERROR_FILE_NOT_FOUND ) {

        switch ( pPropertyTableItem->Format ) {

            case CLUSPROP_FORMAT_DWORD:
            case CLUSPROP_FORMAT_LONG:
                status = ERROR_SUCCESS;
                bytesReturned = sizeof(DWORD);
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_ULARGE_INTEGER:
            case CLUSPROP_FORMAT_LARGE_INTEGER:
                status = ERROR_SUCCESS;
                bytesReturned = sizeof(ULARGE_INTEGER);
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_MULTI_SZ:
                status = ERROR_SUCCESS;
                if ( pPropertyTableItem->Default != 0 ) {
                    bytesReturned = pPropertyTableItem->Minimum;
                } else {
                    bytesReturned = sizeof(WCHAR);
                }
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                status = ERROR_SUCCESS;
                if ( pPropertyTableItem->Default != 0 ) {
                    bytesReturned = (lstrlenW((LPCWSTR)pPropertyTableItem->lpDefault) + 1) * sizeof(WCHAR);
                } else {
                    bytesReturned = (lstrlenW(CLRTL_NULL_STRING) + 1) * sizeof(WCHAR);
                }
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_BINARY:
                status = ERROR_SUCCESS;
                if ( pPropertyTableItem->Default != 0 ) {
                    bytesReturned = pPropertyTableItem->Minimum;
                } else {
                    bytesReturned = 0;
                }
                valueType = pPropertyTableItem->Format;
                break;

            default:
                valueType = CLUSPROP_FORMAT_UNKNOWN;
                break;
        }
    } else if ( status == ERROR_SUCCESS ) {
        switch ( valueType ) {
            case REG_DWORD:
                if ((pPropertyTableItem->Format == CLUSPROP_FORMAT_DWORD) ||
                    (pPropertyTableItem->Format == CLUSPROP_FORMAT_LONG))
                {
                    valueType = pPropertyTableItem->Format;
                } else {
                    valueType = CLUSPROP_FORMAT_UNKNOWN;
                }
                break;

            case REG_QWORD:
                if ((pPropertyTableItem->Format == CLUSPROP_FORMAT_ULARGE_INTEGER) ||
                    (pPropertyTableItem->Format == CLUSPROP_FORMAT_LARGE_INTEGER))
                {
                    valueType = pPropertyTableItem->Format;
                } else {
                    valueType = CLUSPROP_FORMAT_UNKNOWN;
                }
                break;

            case REG_MULTI_SZ:
                valueType = CLUSPROP_FORMAT_MULTI_SZ;
                break;

            case REG_SZ:
            case REG_EXPAND_SZ:
                //
                // Include the size of the expanded string in both REG_SZ and REG_EXPAND_SZ
                //
                pszValue = ClRtlGetSzValue( (HKEY) key,
                                            rvn.PszName(),
                                            pClusterRegApis );
                if ( pszValue != NULL ) {
                    pszExpanded = ClRtlExpandEnvironmentStrings( pszValue );
                    if ( pszExpanded == NULL ) {
                        status = GetLastError();
                    } else if ( lstrcmpW( pszValue, pszExpanded ) != 0 ) {
                        bytesReturned += ALIGN_CLUSPROP( (lstrlenW( pszExpanded ) + 1) * sizeof( WCHAR ) );
                        bytesReturned += sizeof(CLUSPROP_SZ);
                    }
                    LocalFree( pszValue );
                    LocalFree( pszExpanded );
                }

                if ( valueType == REG_SZ ) {
                    valueType = CLUSPROP_FORMAT_SZ;
                }
                else {
                    valueType = CLUSPROP_FORMAT_EXPAND_SZ;
                }
                break;

            case REG_BINARY:
                valueType = CLUSPROP_FORMAT_BINARY;
                break;

            default:
                valueType = CLUSPROP_FORMAT_UNKNOWN;
                break;
        }
    }

    if ( status == ERROR_FILE_NOT_FOUND ) {
        status = ERROR_SUCCESS;
    } else if ( status == ERROR_SUCCESS ) {
        if ( pPropertyTableItem->Format != valueType ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetPropertySize: Property '%1!ls!' format %2!d! expected, was %3!d!.\n", rvn.PszKeyName(), pPropertyTableItem->Format, valueType );
            status = ERROR_INVALID_PARAMETER;
        } else {
            //assume that the size of dword and long 
            //is fixed to 32 bits
            if (( valueType == CLUSPROP_FORMAT_DWORD ) ||
                ( valueType == CLUSPROP_FORMAT_LONG ))
            {
                headerLength = sizeof(CLUSPROP_PROPERTY_NAME)
                                + ((lstrlenW( pPropertyTableItem->Name ) + 1) * sizeof(WCHAR))
                                + sizeof(CLUSPROP_DWORD)
                                - 4   // CLUSPROP_DWORD.dw (specified by bytesReturned)
                                + sizeof(CLUSPROP_SYNTAX); // for endmark
            } else
            if (( valueType == CLUSPROP_FORMAT_ULARGE_INTEGER ) ||
                ( valueType == CLUSPROP_FORMAT_LARGE_INTEGER ))
            {
                headerLength = sizeof(CLUSPROP_PROPERTY_NAME)
                                + ((lstrlenW( pPropertyTableItem->Name ) + 1) * sizeof(WCHAR))
                                + sizeof(CLUSPROP_ULARGE_INTEGER)
                                - 8   // CLUSPROP_ULARGE_INTEGER.li (specified by bytesReturned)
                                + sizeof(CLUSPROP_SYNTAX); // for endmark
            } else {
                // NOTE: This assumes SZ, EXPAND_SZ, MULTI_SZ, and BINARY are the same size
                headerLength = sizeof(CLUSPROP_PROPERTY_NAME)
                                + ((lstrlenW( pPropertyTableItem->Name ) + 1) * sizeof(WCHAR))
                                + sizeof(CLUSPROP_BINARY)
                                + sizeof(CLUSPROP_SYNTAX); // for endmark
            }

            headerLength = ALIGN_CLUSPROP( headerLength );
            bytesReturned = ALIGN_CLUSPROP( bytesReturned );
            *pcbOutPropertyListSize += (bytesReturned + headerLength);
            *pnPropertyCount += 1;
        }
    }

    //
    // Close the key if we opened it.
    //
    if ( ( rvn.PszKeyName() != NULL ) && 
         ( key != NULL ) ) {
        (*pClusterRegApis->pfnCloseKey)( key );
    }

    return(status);

} // ClRtlGetPropertySize



DWORD
WINAPI
ClRtlGetProperty(
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTableItem,
    OUT PVOID * pOutPropertyItem,
    IN OUT LPDWORD pcbOutPropertyItemSize
    )

/*++

Routine Description:

Arguments:

Return Value:

    ERROR_SUCCESS - 

    ERROR_BAD_ARGUMENTS - 

    ERROR_MORE_DATA - 

Notes:

    The buffer size has already been determined to be large enough to hold
    the return data.

--*/

{
    DWORD   status = ERROR_SUCCESS;
    DWORD   valueType;
    DWORD   bytesReturned;
    DWORD   bufferSize;
    PVOID   dataBuffer;
    DWORD   nameLength;
    PVOID   key = NULL;
    CLUSTER_PROPERTY_FORMAT format;
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR  pszExpanded = NULL;
    CRegistryValueName rvn;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis->pfnQueryValue == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetProperty: hkeyClusterKey or pClusterRegApis->pfnQueryValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    // 
    // Use the wrapper class CRegistryValueName to parse value name to see if it 
    // contains a backslash.
    // 
    status = rvn.ScInit( pPropertyTableItem->Name, pPropertyTableItem->KeyName );
    if ( status != ERROR_SUCCESS )
    {
       return status;
    }

    //
    // If the value resides at a different location, open the key.
    //
    if ( rvn.PszKeyName() != NULL ) {
        if ( (pClusterRegApis->pfnOpenKey == NULL) ||
             (pClusterRegApis->pfnCloseKey == NULL) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetProperty: pClusterRegApis->pfnOpenValue or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
            return(ERROR_BAD_ARGUMENTS);
        }

        status = (*pClusterRegApis->pfnOpenKey)( hkeyClusterKey,
                                                 rvn.PszKeyName(),
                                                 KEY_READ,
                                                 &key);
    } else {
        key = hkeyClusterKey;
    }

    //
    // Find out if this property is available
    //
    if ( status == ERROR_SUCCESS ) {
        status = (*pClusterRegApis->pfnQueryValue)( key,
                                                    rvn.PszName(),
                                                    &valueType,
                                                    NULL,
                                                    &bytesReturned );
    }

    //
    // If the value is not present, return the default value.
    //
    if ( status == ERROR_FILE_NOT_FOUND ) {
        switch ( pPropertyTableItem->Format ) {

            case CLUSPROP_FORMAT_DWORD:
            case CLUSPROP_FORMAT_LONG:
                status = ERROR_SUCCESS;
                bytesReturned = sizeof(DWORD);
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_ULARGE_INTEGER:
            case CLUSPROP_FORMAT_LARGE_INTEGER:
                status = ERROR_SUCCESS;
                bytesReturned = sizeof(ULARGE_INTEGER);
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_MULTI_SZ:
                status = ERROR_SUCCESS;
                if ( pPropertyTableItem->Default != 0 ) {
                    bytesReturned = pPropertyTableItem->Minimum;
                } else {
                    bytesReturned = sizeof(WCHAR);
                }
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                status = ERROR_SUCCESS;
                if ( pPropertyTableItem->Default != 0 ) {
                    bytesReturned = (lstrlenW((LPCWSTR)pPropertyTableItem->lpDefault) + 1) * sizeof(WCHAR);
                } else {
                    bytesReturned = (lstrlenW(CLRTL_NULL_STRING) + 1) * sizeof(WCHAR);
                }
                valueType = pPropertyTableItem->Format;
                break;

            case CLUSPROP_FORMAT_BINARY:
                status = ERROR_SUCCESS;
                if ( pPropertyTableItem->Default != 0 ) {
                    bytesReturned = pPropertyTableItem->Minimum;
                } else {
                    bytesReturned = 0;
                }
                valueType = pPropertyTableItem->Format;
                break;

            default:
                valueType = CLUSPROP_FORMAT_UNKNOWN;
                break;
        }
    }

    if ( status == ERROR_SUCCESS ) {
        //
        // Get the property format
        //
        switch ( pPropertyTableItem->Format ) {
            case CLUSPROP_FORMAT_BINARY:
            case CLUSPROP_FORMAT_DWORD:
            case CLUSPROP_FORMAT_LONG:
            case CLUSPROP_FORMAT_ULARGE_INTEGER:
            case CLUSPROP_FORMAT_LARGE_INTEGER:
            case CLUSPROP_FORMAT_MULTI_SZ:
            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                format = (enum CLUSTER_PROPERTY_FORMAT) pPropertyTableItem->Format;
                break;

            default:
                format = CLUSPROP_FORMAT_UNKNOWN;
                break;

        }

        props.pb = (LPBYTE) *pOutPropertyItem;

        //
        // Copy the property name, which includes its syntax and length.
        //
        nameLength = (lstrlenW( pPropertyTableItem->Name ) + 1) * sizeof(WCHAR);
        props.pSyntax->dw = CLUSPROP_SYNTAX_NAME;
        props.pName->cbLength = nameLength;
        lstrcpyW( props.pName->sz, pPropertyTableItem->Name );
        bytesReturned = sizeof(*props.pName) + ALIGN_CLUSPROP( nameLength );
        *pcbOutPropertyItemSize -= bytesReturned;
        props.pb += bytesReturned;

        //
        // Copy the property value header.
        //
        props.pSyntax->wFormat = (USHORT)format;
        props.pSyntax->wType = CLUSPROP_TYPE_LIST_VALUE;

        //
        // Read the property value.
        //
        if ( pPropertyTableItem->Format == CLUSPROP_FORMAT_DWORD ||
             pPropertyTableItem->Format == CLUSPROP_FORMAT_LONG )
        {
            bufferSize = *pcbOutPropertyItemSize
                        - (sizeof(*props.pDwordValue) - sizeof(props.pDwordValue->dw));
            dataBuffer = &props.pDwordValue->dw;
        } else
        if ( pPropertyTableItem->Format == CLUSPROP_FORMAT_ULARGE_INTEGER ||
             pPropertyTableItem->Format == CLUSPROP_FORMAT_ULARGE_INTEGER )
        {
            bufferSize = *pcbOutPropertyItemSize
                        - (sizeof(*props.pULargeIntegerValue) - sizeof(props.pULargeIntegerValue->li));
            dataBuffer = &props.pULargeIntegerValue->li;
        } else {
            // NOTE: This assumes SZ, MULTI_SZ, and BINARY are the same size
            bufferSize = *pcbOutPropertyItemSize - sizeof(*props.pBinaryValue);
            dataBuffer = props.pBinaryValue->rgb;
        }
        bytesReturned = bufferSize;
        if ( key == NULL ) {
            status = ERROR_FILE_NOT_FOUND;
        } else {
            status = (*pClusterRegApis->pfnQueryValue)( key,
                                                        rvn.PszName(),
                                                        &valueType,
                                                        (LPBYTE) dataBuffer,
                                                        &bytesReturned );
        }

        //
        // If the value is not present, return the default value.
        //
        if ( status == ERROR_FILE_NOT_FOUND ) {
            switch ( pPropertyTableItem->Format ) {

                case CLUSPROP_FORMAT_DWORD:
                case CLUSPROP_FORMAT_LONG:
                    //assume size of dword and long is the same
                    status = ERROR_SUCCESS;
                    bytesReturned = sizeof(DWORD);
                    props.pDwordValue->dw = pPropertyTableItem->Default;
                    break;

                case CLUSPROP_FORMAT_ULARGE_INTEGER:
                case CLUSPROP_FORMAT_LARGE_INTEGER:
                    status = ERROR_SUCCESS;
                    bytesReturned = sizeof(ULARGE_INTEGER);
                    props.pULargeIntegerValue->li.u = pPropertyTableItem->ULargeIntData->Default.u;
                    break;

                case CLUSPROP_FORMAT_MULTI_SZ:
                    status = ERROR_SUCCESS;
                    if ( pPropertyTableItem->Default != 0 ) {
                        bytesReturned = pPropertyTableItem->Minimum;
                        if ( bufferSize < bytesReturned ) {
                            CopyMemory( dataBuffer, (LPCWSTR)pPropertyTableItem->lpDefault, bytesReturned );
                        }
                    } else {
                        bytesReturned = sizeof(WCHAR);
                    }
                    break;

                case CLUSPROP_FORMAT_SZ:
                case CLUSPROP_FORMAT_EXPAND_SZ:
                    status = ERROR_SUCCESS;
                    if ( pPropertyTableItem->Default != 0 ) {
                        bytesReturned = (lstrlenW((LPCWSTR)pPropertyTableItem->lpDefault) + 1) * sizeof(WCHAR);
                        if ( bufferSize < bytesReturned ) {
                            lstrcpyW( (LPWSTR) dataBuffer, (LPCWSTR)pPropertyTableItem->lpDefault );
                        }
                    } else {
                        bytesReturned = (lstrlenW(CLRTL_NULL_STRING) + 1) * sizeof(WCHAR);
                    }
                    break;

                case CLUSPROP_FORMAT_BINARY:
                    status = ERROR_SUCCESS;
                    if ( pPropertyTableItem->Default != 0 ) {
                        bytesReturned = pPropertyTableItem->Minimum;
                        if ( bufferSize < bytesReturned ) {
                            CopyMemory( dataBuffer, (LPBYTE)pPropertyTableItem->lpDefault, bytesReturned );
                        }
                    } else {
                        bytesReturned = 0;
                    }
                    break;

                default:
                    break;
            }
        }

        if ( bufferSize < bytesReturned ) {
            status = ERROR_MORE_DATA;
        } else if ( status == ERROR_SUCCESS ) {
            props.pValue->cbLength = bytesReturned;

            // Round the bytes used up to the next DWORD boundary.
            bytesReturned = ALIGN_CLUSPROP( bytesReturned );

            bytesReturned += sizeof(*props.pValue);
            props.pb += bytesReturned;

            //
            // If this is an SZ or EXPAND_SZ, see if the expanded value should
            // be added to the value list.
            //
            if (    ( pPropertyTableItem->Format == CLUSPROP_FORMAT_SZ )
                ||  ( pPropertyTableItem->Format == CLUSPROP_FORMAT_EXPAND_SZ ) ) {
                pszExpanded = ClRtlExpandEnvironmentStrings( (LPCWSTR) dataBuffer );
                if ( pszExpanded == NULL ) {
                    status = GetLastError();
                } else {
                    if ( lstrcmpiW( pszExpanded, (LPCWSTR) dataBuffer ) != 0 ) {
                        props.pSyntax->dw = CLUSPROP_SYNTAX_LIST_VALUE_EXPANDED_SZ;
                        bufferSize = ALIGN_CLUSPROP( (lstrlenW( pszExpanded ) + 1) * sizeof( WCHAR ) );
                        props.pStringValue->cbLength = bufferSize;
                        lstrcpyW( props.pStringValue->sz, pszExpanded );
                        bytesReturned += sizeof( *props.pStringValue ) + bufferSize;
                        props.pb += sizeof( *props.pStringValue ) + bufferSize;
                    }
                    LocalFree( pszExpanded );
                }
            }

            if ( status == ERROR_SUCCESS ) {
                // Add the value list endmark.
                props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
                props.pb += sizeof(*props.pSyntax);
                bytesReturned += sizeof(*props.pSyntax);

                *pcbOutPropertyItemSize -= bytesReturned;
                *pOutPropertyItem = (PVOID)props.pb;
            } // if:  ERROR_SUCCESS
        } // else if:  ERROR_SUCCESS
    } // if:  ERROR_SUCCESS

    if ( status == ERROR_FILE_NOT_FOUND ) {
        status = ERROR_SUCCESS;
    }

    //
    // Close the key if we opened it.
    //
    if ( (rvn.PszKeyName() != NULL) &&
         (key != NULL) ) {
        (*pClusterRegApis->pfnCloseKey)( key );
    }

    return(status);

} // ClRtlGetProperty



DWORD
WINAPI
ClRtlpSetPropertyTable(
    IN HANDLE hXsaction,
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    IN PVOID Reserved,
    IN BOOL bAllowUnknownProperties,
    IN const PVOID pInPropertyList,
    IN DWORD cbInPropertyListSize,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

Arguments:

    hkeyClusterKey - The opened cluster database key where properties are to
        be written.  If not specified, the property list will only be
        validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    Reserved - Reserved for future use.

    bAllowUnknownProperties - Don't fail if unknown properties are found.

    pInPropertyList - The input buffer.

    cbInPropertyListSize - The input buffer size.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input buffer and the parameter block.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_BAD_ARGUMENTS - hkeyClusterKey is specified but proper cluster
        registry APIs are not specified, or no property table is specified.

    ERROR_INVALID_DATA - No property list is specified or the format of the
        property list is invalid.

    ERROR_INSUFFICIENT_BUFFER - The property list buffer isn't large enough to
        contain all the data it indicates it should contain.

    ERROR_INVALID_PARAMETER - The property list isn't formatted properly.

    A Win32 Error on failure.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    PRESUTIL_PROPERTY_ITEM  propertyItem;
    DWORD                   inBufferSize;
    DWORD                   itemCount;
    DWORD                   dataSize;
    PVOID                   key;
    CLUSPROP_BUFFER_HELPER  buf;
    PCLUSPROP_PROPERTY_NAME pName;
    CRegistryValueName      rvn;

    if ( ( (hkeyClusterKey != NULL) &&
           (pClusterRegApis->pfnSetValue == NULL) ) ||
         ( pPropertyTable == NULL ) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: hkeyClusterKey or pClusterRegApis->pfnSetValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    if ( pInPropertyList == NULL ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: pInPropertyList == NULL. Returning ERROR_INVALID_DATA\n" );
        return(ERROR_INVALID_DATA);
    }

    buf.pb = (LPBYTE) pInPropertyList;
    inBufferSize = cbInPropertyListSize;

    //
    // Get the number of items in this list
    //
    if ( inBufferSize < sizeof(DWORD) ) {
        return(ERROR_INSUFFICIENT_BUFFER);
    }

    itemCount = buf.pList->nPropertyCount;
    buf.pdw++;
    inBufferSize -= sizeof(*buf.pdw);

    //
    // Parse the rest of the items in the buffer.
    //
    while ( itemCount-- ) {
        //
        // Verify that the buffer is big enough to contain the
        // property name and a value.
        //
        pName = buf.pName;
        if ( inBufferSize < sizeof(*pName) ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }
        dataSize = sizeof(*pName) + ALIGN_CLUSPROP( pName->cbLength );
        if ( inBufferSize < dataSize + sizeof(CLUSPROP_VALUE) ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }

        //
        // Verify that the syntax of the property name is correct.
        //
        if ( pName->Syntax.dw != CLUSPROP_SYNTAX_NAME ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: not a name syntax.\n" );
            return(ERROR_INVALID_PARAMETER);
        }

        //
        // Verify that the length is correct for the string.
        //
        if ( pName->cbLength != (lstrlenW( pName->sz ) + 1) * sizeof(WCHAR) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: name is not a valid C string.\n" );
            return(ERROR_INVALID_DATA);
        }

        //
        // Move the buffer pointer to the property value.
        //
        buf.pb += dataSize;
        inBufferSize -= dataSize;

        //
        // Find the property name in the list of known properties.
        //
        propertyItem = pPropertyTable;
        while ( propertyItem->Name != NULL ) {

            if ( lstrcmpiW( pName->sz, propertyItem->Name ) == 0 ) {
                //
                // Verify that the buffer is big enough to contain the value.
                //
                dataSize = sizeof(*buf.pValue)
                            + ALIGN_CLUSPROP( buf.pValue->cbLength )
                            + sizeof(CLUSPROP_SYNTAX); // endmark
                if ( inBufferSize < dataSize ) {
                    return(ERROR_INSUFFICIENT_BUFFER);
                }

                //
                // Verify that the syntax type is LIST_VALUE.
                //
                if ( buf.pSyntax->wType != CLUSPROP_TYPE_LIST_VALUE ) {
                    ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: Property '%1!ls!' type CLUSPROP_TYPE_LIST_VALUE (%2!d!) expected, was %3!d!.\n", pName->sz, CLUSPROP_TYPE_LIST_VALUE, buf.pSyntax->wType );
                    return(ERROR_INVALID_PARAMETER);
                }

                //
                // Verify that this property should be of this format.
                //
                if ( buf.pSyntax->wFormat != propertyItem->Format ) {
                    ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: Property '%1!ls!' format %2!d! expected, was %3!d!.\n", pName->sz, propertyItem->Format, buf.pSyntax->wType );
                    status = ERROR_INVALID_PARAMETER;
                    break;
                }

                //
                // Make sure we are allowed to set this item.
                //
                if ( propertyItem->Flags & RESUTIL_PROPITEM_READ_ONLY ) {
                    ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: Property '%1!ls!' is non-writable.\n", pName->sz );
                    return(ERROR_INVALID_PARAMETER);
                }

                // 
                // Use the wrapper class CRegistryValueName to parse value name to see if it 
                // contains a backslash.
                // 
                status = rvn.ScInit( propertyItem->Name, propertyItem->KeyName );
                if ( status != ERROR_SUCCESS ) {
                    return status;
                }

                //
                // If the value resides at a different location, create the key.
                //
                if ( (hkeyClusterKey != NULL) &&
                     (rvn.PszKeyName() != NULL) ) {

                    DWORD disposition;

                    if ( (pClusterRegApis->pfnCreateKey == NULL) ||
                         (pClusterRegApis->pfnCloseKey == NULL)  ) {
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: pClusterRegApis->pfnCreateKey or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
                        return(ERROR_BAD_ARGUMENTS);
                    }
                    if ( hXsaction != NULL ) {
                        if ( pClusterRegApis->pfnLocalCreateKey == NULL ) {
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: pClusterRegApis->pfnLocalCreateKey == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
                            return(ERROR_BAD_ARGUMENTS);
                        }
                    
                        status = (*pClusterRegApis->pfnLocalCreateKey)(hXsaction, 
                                                               hkeyClusterKey,
                                                               rvn.PszKeyName(),
                                                               0,
                                                               KEY_ALL_ACCESS,
                                                               NULL,
                                                               &key,
                                                               &disposition );
                    }

                    else {
                        status = (*pClusterRegApis->pfnCreateKey)( hkeyClusterKey,
                                                               rvn.PszKeyName(),
                                                               0,
                                                               KEY_ALL_ACCESS,
                                                               NULL,
                                                               &key,
                                                               &disposition );
                    }

                    if ( status != ERROR_SUCCESS ) {
                        return(status);
                    }
                } else {
                    key = hkeyClusterKey;
                }

                //
                // Validate, write, and save the property data.
                //
                switch ( buf.pSyntax->wFormat ) {
                    case CLUSPROP_FORMAT_DWORD:
                        status = ClRtlpSetDwordProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pDwordValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    case CLUSPROP_FORMAT_LONG:
                        status = ClRtlpSetLongProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pLongValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    case CLUSPROP_FORMAT_ULARGE_INTEGER:
                        status = ClRtlpSetULargeIntegerProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pULargeIntegerValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    case CLUSPROP_FORMAT_LARGE_INTEGER:
                        status = ClRtlpSetLargeIntegerProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pLargeIntegerValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    case CLUSPROP_FORMAT_SZ:
                    case CLUSPROP_FORMAT_EXPAND_SZ:
                        status = ClRtlpSetStringProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pStringValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    case CLUSPROP_FORMAT_MULTI_SZ:
                        status = ClRtlpSetMultiStringProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pStringValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    case CLUSPROP_FORMAT_BINARY:
                        status = ClRtlpSetBinaryProperty(
                                    hXsaction,
                                    key,
                                    pClusterRegApis,
                                    propertyItem,
                                    rvn,
                                    buf.pBinaryValue,
                                    bForceWrite,
                                    pOutParams );
                        break;

                    default:
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: Property '%1!ls!' unknown format %2!d! specified.\n", pName->sz, buf.pSyntax->wFormat );
                        status = ERROR_INVALID_PARAMETER;
                        break;

                } // switch:  value data format

                //
                // Close the key if we opened it.
                //
                if ( (hkeyClusterKey != NULL) &&
                     (rvn.PszKeyName() != NULL) ) {
                    (*pClusterRegApis->pfnCloseKey)( key );
                }

                //
                // If an error occurred processing the property, cleanup and return.
                //
                if ( status != ERROR_SUCCESS ) {
                    return(status);
                }

                //
                // Move the buffer past the value.
                //
                buf.pb += dataSize;
                inBufferSize -= dataSize;

                break;

            } else {
                propertyItem++;
                //
                // If we reached the end of the list, then return failure
                // if we do not allow unknown properties.
                //
                if ( (propertyItem->Name == NULL) &&
                     ! bAllowUnknownProperties ) {
                    ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: Property '%1!ls!' not found.\n", pName->sz );
                    return(ERROR_INVALID_PARAMETER);
                }
            }

        }

        //
        // If no property name was found, this is an invalid parameter if
        // we don't allow unknown properties.  Otherwise advance past the
        // property value.
        //
        if ( propertyItem->Name == NULL) {
            if ( ! bAllowUnknownProperties ) {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPropertyTable: Property '%1!ls!' not found.\n", pName->sz );
                return(ERROR_INVALID_PARAMETER);
            }

            //
            // Advance the buffer pointer past the value in the value list.
            //
            while ( (buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (inBufferSize > 0) ) {
                // ASSERT(inBufferSize > sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength));
                buf.pb += sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
                inBufferSize -= sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
            }  // while:  more values in the list

            //
            // Advance the buffer pointer past the value list endmark.
            //
            // ASSERT(inBufferSize >= sizeof(*buf.pSyntax));
            buf.pb += sizeof(*buf.pSyntax); // endmark
            inBufferSize -= sizeof(*buf.pSyntax);
        }
    }

    //
    // Now find any parameters that are not represented in the property
    // table. All of these extra properties will just be set without validation.
    //
    if ( (status == ERROR_SUCCESS) &&
         (pInPropertyList != NULL) &&
         bAllowUnknownProperties ) {
        status = ClRtlpSetNonPropertyTable( hXsaction,
                                            hkeyClusterKey,
                                            pClusterRegApis,
                                            pPropertyTable,
                                            NULL,
                                            pInPropertyList,
                                            cbInPropertyListSize );
    }

    return(status);

} // ClRtlpSetPropertyTable



static
DWORD
WINAPI
ClRtlpSetDwordProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_DWORD pInDwordValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a DWORD property, write it to the cluster database (or delete it
    if it is zero length), and save it in the specified parameter block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInDwordValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD   status = ERROR_SUCCESS;
    BOOL    bZeroLengthData;
    DWORD * pdwValue;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInDwordValue->cbLength == 0 );

        //
        // Validate the property data if not zero length.
        //
        if ( ! bZeroLengthData ) {
            //
            // Verify the length of the value.
            //
            if ( pInDwordValue->cbLength != sizeof(DWORD) ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetDwordProperty: Property '%1!ls!' length %2!d! not DWORD length.\n", rrvnModifiedNames.PszName(), pInDwordValue->cbLength );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  data in value not size of DWORD

            //
            // Verify that the value is within the valid range.
            //
            if ( (      (pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  ((LONG) pInDwordValue->dw > (LONG) pPropertyItem->Maximum))
                || (    !(pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  (pInDwordValue->dw > pPropertyItem->Maximum)) ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetDwordProperty: Property '%1!ls!' value %2!u! too large.\n", rrvnModifiedNames.PszName(), pInDwordValue->dw );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value too high
            if ( (      (pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  ((LONG) pInDwordValue->dw < (LONG) pPropertyItem->Minimum))
                || (    !(pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  (pInDwordValue->dw < pPropertyItem->Minimum)) ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetDwordProperty: Property '%1!ls!' value %2!u! too small.\n", rrvnModifiedNames.PszName(), pInDwordValue->dw );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value to low
        } // if:  non-zero length data

        pdwValue = (DWORD *) &pOutParams[pPropertyItem->Offset];

        //
        // Write the value to the cluster database.
        // If the data length is zero, delete the value.
        //
        if ( hkey != NULL ) {
            if ( bZeroLengthData ) {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } else {
                    status = (*pClusterRegApis->pfnDeleteValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } // if/else:  doing/not doing a transaction

                //
                // If the property doesn't exist in the
                // cluster database, fix the status.
                //
                if ( status == ERROR_FILE_NOT_FOUND ) {
                    status = ERROR_SUCCESS;
                } // if:  property already doesn't exist
            } else {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalSetValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_DWORD,
                                    (CONST BYTE *) &pInDwordValue->dw,
                                    sizeof(DWORD) );
                } else {
                    status = (*pClusterRegApis->pfnSetValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_DWORD,
                                    (CONST BYTE *) &pInDwordValue->dw,
                                    sizeof(DWORD) );
                } // if/else:  doing/not doing a transaction
            } // if/else:  zero length data
        } // if:  writing data

        //
        // Save the value to the output Parameter block.
        // If the data length is zero, set to the default.
        //
        if (    (status == ERROR_SUCCESS)
            &&  (pOutParams != NULL) ) {
            if ( bZeroLengthData ) {
                *pdwValue = pPropertyItem->Default;
            } else {
                *pdwValue = pInDwordValue->dw;
            } // if/else:  zero length data
        } // if:  data written successfully and parameter block specified
    } while ( 0 );

    return status;

} // ClRtlpSetDwordProperty



static
DWORD
WINAPI
ClRtlpSetLongProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_LONG pInLongValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a LONG property, write it to the cluster database (or delete it
    if it is zero length), and save it in the specified parameter block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInLongValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD   status = ERROR_SUCCESS;
    BOOL    bZeroLengthData;
    LONG *  plValue;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInLongValue->cbLength == 0 );

        //
        // Validate the property data if not zero length.
        //
        if ( ! bZeroLengthData ) {
            //
            // Verify the length of the value.
            //
            if ( pInLongValue->cbLength != sizeof(LONG) ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetLongProperty: Property '%1!ls!' length %2!d! not LONG length.\n", rrvnModifiedNames.PszName(), pInLongValue->cbLength );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  data in value not size of LONG

            //
            // Verify that the value is within the valid range.
            //
            if ( pInLongValue->l > (LONG) pPropertyItem->Maximum ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetLongProperty: Property '%1!ls!' value %2!d! too large.\n", rrvnModifiedNames.PszName(), pInLongValue->l );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value too high
            if ( pInLongValue->l < (LONG) pPropertyItem->Minimum ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetLongProperty: Property '%1!ls!' value %2!d! too small.\n", rrvnModifiedNames.PszName(), pInLongValue->l );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value too small
        } // if:  non-zero length data

        plValue = (LONG *) &pOutParams[pPropertyItem->Offset];

        //
        // Write the value to the cluster database.
        // If the data length is zero, delete the value.
        //
        if ( hkey != NULL ) {
            if ( bZeroLengthData ) {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } else {
                    status = (*pClusterRegApis->pfnDeleteValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } // if/else:  doing/not doing a transaction

                //
                // If the property doesn't exist in the
                // cluster database, fix the status.
                //
                if ( status == ERROR_FILE_NOT_FOUND ) {
                    status = ERROR_SUCCESS;
                } // if:  property already doesn't exist
            } else {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalSetValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_DWORD,
                                    (CONST BYTE *) &pInLongValue->l,
                                    sizeof(LONG) );
                } else {
                    status = (*pClusterRegApis->pfnSetValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_DWORD,
                                    (CONST BYTE *) &pInLongValue->l,
                                    sizeof(LONG) );
                } // if/else:  doing/not doing a transaction
            } // if/else:  zero length data
        } // if:  writing data

        //
        // Save the value to the output Parameter block.
        // If the data length is zero, set to the default.
        //
        if (    (status == ERROR_SUCCESS)
            &&  (pOutParams != NULL) ) {
            if ( bZeroLengthData ) {
                *plValue = (LONG) pPropertyItem->Default;
            } else {
                *plValue = pInLongValue->l;
            } // if/else:  zero length data
        } // if:  data written successfully and parameter block specified
    } while ( 0 );

    return status;

} // ClRtlpSetLongProperty



static
DWORD
WINAPI
ClRtlpSetULargeIntegerProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_ULARGE_INTEGER pInULargeIntegerValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a ULARGE_INTEGER property, write it to the cluster database (or
    delete it if it is zero length), and save it in the specified parameter
    block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInULargeIntegerValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD               status = ERROR_SUCCESS;
    BOOL                bZeroLengthData;
    ULARGE_INTEGER *    pullValue;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInULargeIntegerValue->cbLength == 0 );

        //
        // Validate the property data if not zero length.
        //
        if ( ! bZeroLengthData ) {
            //
            // Verify the length of the value.
            //
            if ( pInULargeIntegerValue->cbLength != sizeof(ULARGE_INTEGER) ) {
                ClRtlDbgPrint(LOG_UNUSUAL,
                              "ClRtlpSetULargeIntegerProperty: Property '%1!ls!' length %2!d! "
                              "not ULARGE_INTEGER length.\n",
                              rrvnModifiedNames.PszName(),
                              pInULargeIntegerValue->cbLength );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  data in value not size of DWORD

            //
            // Verify that the value is within the valid range.
            //
            if ( (      (pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  ((LONGLONG)pInULargeIntegerValue->li.QuadPart > (LONGLONG)pPropertyItem->ULargeIntData->Maximum.QuadPart))
                || (    !(pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  (pInULargeIntegerValue->li.QuadPart > pPropertyItem->ULargeIntData->Maximum.QuadPart)) )
            {
                ClRtlDbgPrint(
                              LOG_UNUSUAL,
                              "ClRtlpSetULargeIntegerProperty: Property '%1!ls!' value %2!I64u! "
                              "too large.\n",
                              rrvnModifiedNames.PszName(),
                              pInULargeIntegerValue->li.QuadPart );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value too high
            if ( (      (pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  ((LONGLONG)pInULargeIntegerValue->li.QuadPart < (LONGLONG)pPropertyItem->ULargeIntData->Minimum.QuadPart))
                || (    !(pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  (pInULargeIntegerValue->li.QuadPart < pPropertyItem->ULargeIntData->Minimum.QuadPart)) )
            {
                ClRtlDbgPrint(LOG_UNUSUAL,
                              "ClRtlpSetULargeIntegerProperty: Property '%1!ls!' value "
                              "%2!I64u! too small.\n",
                              rrvnModifiedNames.PszName(),
                              pInULargeIntegerValue->li.QuadPart );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value to low
        } // if:  non-zero length data

        pullValue = (ULARGE_INTEGER *) &pOutParams[pPropertyItem->Offset];

        //
        // Write the value to the cluster database.
        // If the data length is zero, delete the value.
        //
        if ( hkey != NULL ) {
            if ( bZeroLengthData ) {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } else {
                    status = (*pClusterRegApis->pfnDeleteValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } // if/else:  doing/not doing a transaction

                //
                // If the property doesn't exist in the
                // cluster database, fix the status.
                //
                if ( status == ERROR_FILE_NOT_FOUND ) {
                    status = ERROR_SUCCESS;
                } // if:  property already doesn't exist
            } else {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalSetValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_QWORD,
                                    (CONST BYTE *) &pInULargeIntegerValue->li.QuadPart,
                                    sizeof(ULARGE_INTEGER) );
                } else {
                    status = (*pClusterRegApis->pfnSetValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_QWORD,
                                    (CONST BYTE *) &pInULargeIntegerValue->li.QuadPart,
                                    sizeof(ULARGE_INTEGER) );
                } // if/else:  doing/not doing a transaction
            } // if/else:  zero length data
        } // if:  writing data

        //
        // Save the value to the output Parameter block.
        // If the data length is zero, set to the default.
        //
        if (    (status == ERROR_SUCCESS)  &&  (pOutParams != NULL) ) {
            if ( bZeroLengthData ) {
                pullValue->u = pPropertyItem->ULargeIntData->Default.u;
            } else {
                pullValue->u = pInULargeIntegerValue->li.u;
            } // if/else:  zero length data
        } // if:  data written successfully and parameter block specified
    } while ( 0 );

    return status;

} // ClRtlpSetULargeIntegerProperty


static
DWORD
WINAPI
ClRtlpSetLargeIntegerProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_LARGE_INTEGER pInLargeIntegerValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a LARGE_INTEGER property, write it to the cluster database (or
    delete it if it is zero length), and save it in the specified parameter
    block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInLargeIntegerValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD           status = ERROR_SUCCESS;
    BOOL            bZeroLengthData;
    LARGE_INTEGER * pllValue;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInLargeIntegerValue->cbLength == 0 );

        //
        // Validate the property data if not zero length.
        //
        if ( ! bZeroLengthData ) {
            //
            // Verify the length of the value.
            //
            if ( pInLargeIntegerValue->cbLength != sizeof(LARGE_INTEGER) ) {
                ClRtlDbgPrint(LOG_UNUSUAL,
                              "ClRtlpSetLargeIntegerProperty: Property '%1!ls!' length %2!d! "
                              "not LARGE_INTEGER length.\n",
                              rrvnModifiedNames.PszName(),
                              pInLargeIntegerValue->cbLength );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  data in value not size of DWORD

            //
            // Verify that the value is within the valid range.
            //
            if ( (      (pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  ((LONGLONG)pInLargeIntegerValue->li.QuadPart > (LONGLONG)pPropertyItem->LargeIntData->Maximum.QuadPart))
                || (    !(pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  (pInLargeIntegerValue->li.QuadPart > pPropertyItem->LargeIntData->Maximum.QuadPart)) )
            {
                ClRtlDbgPrint(LOG_UNUSUAL,
                              "ClRtlpSetLargeIntegerProperty: Property '%1!ls!' value %2!I64d! "
                              "too large.\n",
                              rrvnModifiedNames.PszName(),
                              pInLargeIntegerValue->li.QuadPart );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value too high
            if ( (      (pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  ((LONGLONG)pInLargeIntegerValue->li.QuadPart < (LONGLONG)pPropertyItem->LargeIntData->Minimum.QuadPart))
                || (    !(pPropertyItem->Flags & RESUTIL_PROPITEM_SIGNED)
                    &&  (pInLargeIntegerValue->li.QuadPart < pPropertyItem->LargeIntData->Minimum.QuadPart)) )
            {
                ClRtlDbgPrint(LOG_UNUSUAL,
                              "ClRtlpSetLargeIntegerProperty: Property '%1!ls!' value "
                              "%2!I64d! too small.\n",
                              rrvnModifiedNames.PszName(),
                              pInLargeIntegerValue->li.QuadPart );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  value to low
        } // if:  non-zero length data

        pllValue = (LARGE_INTEGER *) &pOutParams[pPropertyItem->Offset];

        //
        // Write the value to the cluster database.
        // If the data length is zero, delete the value.
        //
        if ( hkey != NULL ) {
            if ( bZeroLengthData ) {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } else {
                    status = (*pClusterRegApis->pfnDeleteValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName() );
                } // if/else:  doing/not doing a transaction

                //
                // If the property doesn't exist in the
                // cluster database, fix the status.
                //
                if ( status == ERROR_FILE_NOT_FOUND ) {
                    status = ERROR_SUCCESS;
                } // if:  property already doesn't exist
            } else {
                if ( hXsaction ) {
                    status = (*pClusterRegApis->pfnLocalSetValue)(
                                    hXsaction,
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_QWORD,
                                    (CONST BYTE *) &pInLargeIntegerValue->li.QuadPart,
                                    sizeof(LARGE_INTEGER) );
                } else {
                    status = (*pClusterRegApis->pfnSetValue)(
                                    hkey,
                                    rrvnModifiedNames.PszName(),
                                    REG_QWORD,
                                    (CONST BYTE *) &pInLargeIntegerValue->li.QuadPart,
                                    sizeof(LARGE_INTEGER) );
                } // if/else:  doing/not doing a transaction
            } // if/else:  zero length data
        } // if:  writing data

        //
        // Save the value to the output Parameter block.
        // If the data length is zero, set to the default.
        //
        if (    (status == ERROR_SUCCESS)  &&  (pOutParams != NULL) ) {
            if ( bZeroLengthData ) {
                pllValue->u = pPropertyItem->LargeIntData->Default.u;
            } else {
                pllValue->u = pInLargeIntegerValue->li.u;
            } // if/else:  zero length data
        } // if:  data written successfully and parameter block specified
    } while ( 0 );

    return status;

} // ClRtlpSetLargeIntegerProperty


static
DWORD
WINAPI
ClRtlpSetStringProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_SZ pInStringValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a string property (SZ or EXPAND_SZ), write it to the cluster
    database (or delete it if it is zero length), and save it in the
    specified parameter block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInStringValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD               status = ERROR_SUCCESS;
    BOOL                bZeroLengthData;
    LPWSTR UNALIGNED *  ppszValue;
    DWORD               dwType;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInStringValue->cbLength == 0 );

        //
        // Validate the property data if not zero length.
        //
        if ( ! bZeroLengthData ) {
            //
            // Verify the length of the value.
            //
            if ( pInStringValue->cbLength != (lstrlenW( pInStringValue->sz ) + 1) * sizeof(WCHAR) ) {
                ClRtlDbgPrint( LOG_UNUSUAL, "ClRtlpSetStringProperty: Property '%1!ls!' length %2!d! doesn't match zero-term. length.\n", rrvnModifiedNames.PszName(), pInStringValue->cbLength );
                status = ERROR_INVALID_DATA;
                break;
            } // if:  string length doesn't match length in property
        } // if:  non-zero length data

        ppszValue = (LPWSTR UNALIGNED *) &pOutParams[pPropertyItem->Offset];

        //
        // If the data changed, write it and save it.
        // Do this even if only the case of the data changed.
        //
        if (    (pOutParams == NULL)
            ||  (*ppszValue == NULL)
            ||  bZeroLengthData
            ||  bForceWrite
            ||  (lstrcmpW( *ppszValue, pInStringValue->sz ) != 0) ) {

            //
            // Write the value to the cluster database.
            // If the data length is zero, delete the value.
            //
            if ( hkey != NULL ) {
                if ( bZeroLengthData ) {
                    if ( hXsaction ) {
                        status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                        hXsaction,
                                        hkey,
                                        rrvnModifiedNames.PszName() );
                    } else {
                        status = (*pClusterRegApis->pfnDeleteValue)(
                                        hkey,
                                        rrvnModifiedNames.PszName() );
                    } // if/else:  doing/not doing a transaction

                    //
                    // If the property doesn't exist in the
                    // cluster database, fix the status.
                    //
                    if ( status == ERROR_FILE_NOT_FOUND ) {
                        status = ERROR_SUCCESS;
                    } // if:  property already doesn't exist
                } else {
                    if ( pPropertyItem->Format == CLUSPROP_FORMAT_EXPAND_SZ ) {
                        dwType = REG_EXPAND_SZ;
                    } else {
                        dwType = REG_SZ;
                    } // if/else:  property format is EXPAND_SZ
                    if ( hXsaction ) {
                        status = (*pClusterRegApis->pfnLocalSetValue)(
                                        hXsaction,
                                        hkey,
                                        rrvnModifiedNames.PszName(),
                                        dwType,
                                        (CONST BYTE *) &pInStringValue->sz,
                                        pInStringValue->cbLength );
                    } else {
                        status = (*pClusterRegApis->pfnSetValue)(
                                        hkey,
                                        rrvnModifiedNames.PszName(),
                                        dwType,
                                        (CONST BYTE *) &pInStringValue->sz,
                                        pInStringValue->cbLength );
                    } // if/else:  doing/not doing a transaction
                } // if/else:  zero length data
            } // if:  writing data

            //
            // Save the value to the output Parameter block.
            // If the data length is zero, set to the default.
            //
            if (    (status == ERROR_SUCCESS)
                &&  (pOutParams != NULL) ) {

                if ( *ppszValue != NULL ) {
                    LocalFree( *ppszValue );
                } // if:  previous value in parameter block

                if ( bZeroLengthData ) {
                    // If a default is specified, copy it.
                    if ( pPropertyItem->lpDefault != NULL ) {
                        *ppszValue = (LPWSTR) LocalAlloc(
                                                  LMEM_FIXED,
                                                  (lstrlenW( (LPCWSTR) pPropertyItem->lpDefault ) + 1) * sizeof(WCHAR)
                                                  );
                        if ( *ppszValue == NULL ) {
                            status = GetLastError();
                            ClRtlDbgPrint(
                                LOG_CRITICAL,
                                "ClRtlpSetStringProperty: error allocating memory for default "
                                "SZ value '%1!ls!' in parameter block for property '%2!ls!'.\n",
                                pPropertyItem->lpDefault,
                                rrvnModifiedNames.PszName() );
                            break;
                        } // if:  error allocating memory
                        lstrcpyW( *ppszValue, (LPCWSTR) pPropertyItem->lpDefault );
                    } else {
                        *ppszValue = NULL;
                    } // if/else:  default value specified
                } else {
                    *ppszValue = (LPWSTR) LocalAlloc( LMEM_FIXED, pInStringValue->cbLength );
                    if ( *ppszValue == NULL ) {
                        status = GetLastError();
                        ClRtlDbgPrint(
                            LOG_CRITICAL,
                            "ClRtlpSetStringProperty: error allocating memory for SZ "
                            "value '%1!ls!' in parameter block for property '%2!ls!'.\n",
                            pInStringValue->cbLength,
                            rrvnModifiedNames.PszName() );
                        break;
                    } // if:  error allocating memory
                    lstrcpyW( *ppszValue, pInStringValue->sz );
                } // if/else:  zero length data
            } // if:  data written successfully and parameter block specified
        } // if:  value changed or zero-length value
    } while ( 0 );

    return status;

} // ClRtlpSetStringProperty



static
DWORD
WINAPI
ClRtlpSetMultiStringProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_MULTI_SZ pInMultiStringValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a MULTI_SZ property, write it to the cluster database (or delete
    it if it is zero length), and save it in the specified parameter block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInMultiStringValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD               status = ERROR_SUCCESS;
    BOOL                bZeroLengthData;
    LPWSTR UNALIGNED *  ppszValue;
    DWORD *             pdwValue;
    DWORD               dwType;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInMultiStringValue->cbLength == 0 );

        ppszValue = (LPWSTR UNALIGNED *) &pOutParams[pPropertyItem->Offset];
        pdwValue = (DWORD *) &pOutParams[pPropertyItem->Offset + sizeof(LPWSTR *)];

        //
        // If the data changed, write it and save it.
        // Do this even if only the case of the data changed.
        //
        if (    (pOutParams == NULL)
            ||  (*ppszValue == NULL)
            ||  (*pdwValue != pInMultiStringValue->cbLength)
            ||  bZeroLengthData
            ||  bForceWrite
            ||  (memcmp( *ppszValue, pInMultiStringValue->sz, *pdwValue ) != 0) ) {

            //
            // Write the value to the cluster database.
            // If the data length is zero, delete the value.
            //
            if ( hkey != NULL ) {
                if ( bZeroLengthData ) {
                    if ( hXsaction ) {
                        status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                        hXsaction,
                                        hkey,
                                        rrvnModifiedNames.PszName() );
                    } else {
                        status = (*pClusterRegApis->pfnDeleteValue)(
                                        hkey,
                                        rrvnModifiedNames.PszName() );
                    } // if/else:  doing/not doing a transaction

                    //
                    // If the property doesn't exist in the
                    // cluster database, fix the status.
                    //
                    if ( status == ERROR_FILE_NOT_FOUND ) {
                        status = ERROR_SUCCESS;
                    } // if:  property already doesn't exist
                } else {
                    if ( pPropertyItem->Format == CLUSPROP_FORMAT_MULTI_SZ ) {
                        dwType = REG_MULTI_SZ;
                    } else {
                        dwType = REG_SZ;
                    } // if/else:  property format is EXPAND_SZ
                    if ( hXsaction ) {
                        status = (*pClusterRegApis->pfnLocalSetValue)(
                                        hXsaction,
                                        hkey,
                                        rrvnModifiedNames.PszName(),
                                        dwType,
                                        (CONST BYTE *) &pInMultiStringValue->sz,
                                        pInMultiStringValue->cbLength );
                    } else {
                        status = (*pClusterRegApis->pfnSetValue)(
                                        hkey,
                                        rrvnModifiedNames.PszName(),
                                        dwType,
                                        (CONST BYTE *) &pInMultiStringValue->sz,
                                        pInMultiStringValue->cbLength );
                    } // if/else:  doing/not doing a transaction
                } // if/else:  zero length data
            } // if:  writing data

            //
            // Save the value to the output Parameter block.
            // If the data length is zero, set to the default.
            //
            if (    (status == ERROR_SUCCESS)
                &&  (pOutParams != NULL) ) {

                if ( *ppszValue != NULL ) {
                    LocalFree( *ppszValue );
                } // if:  previous value in parameter block

                if ( bZeroLengthData ) {
                    // If a default is specified, copy it.
                    if ( pPropertyItem->lpDefault != NULL ) {
                        *ppszValue = (LPWSTR) LocalAlloc( LMEM_FIXED, pPropertyItem->Minimum );
                        if ( *ppszValue == NULL ) {
                            status = GetLastError();
                            *pdwValue = 0;
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetMultiStringProperty: error allocating memory for default MULTI_SZ value in parameter block for property '%1!ls!'.\n", rrvnModifiedNames.PszName() );
                            break;
                        } // if:  error allocating memory
                        CopyMemory( *ppszValue, pPropertyItem->lpDefault, pPropertyItem->Minimum );
                        *pdwValue = pPropertyItem->Minimum;
                    } else {
                        *ppszValue = NULL;
                        *pdwValue = 0;
                    } // if/else:  default value specified
                } else {
                    *ppszValue = (LPWSTR) LocalAlloc( LMEM_FIXED, pInMultiStringValue->cbLength );
                    if ( *ppszValue == NULL ) {
                        status = GetLastError();
                        *pdwValue = 0;
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetMultiStringProperty: error allocating memory for MULTI_SZ value in parameter block for property '%1!ls!'.\n", rrvnModifiedNames.PszName() );
                        break;
                    } // if:  error allocating memory
                    CopyMemory( *ppszValue, pInMultiStringValue->sz, pInMultiStringValue->cbLength );
                    *pdwValue = pInMultiStringValue->cbLength;
                } // if/else:  zero length data
            } // if:  data written successfully and parameter block specified
        } // if:  value changed or zero-length value
    } while ( 0 );

    return status;

} // ClRtlpSetMultiStringProperty



static
DWORD
WINAPI
ClRtlpSetBinaryProperty(
    IN HANDLE hXsaction,
    IN PVOID hkey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyItem,
    IN const CRegistryValueName & rrvnModifiedNames,
    IN PCLUSPROP_BINARY pInBinaryValue,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

    Validate a BINARY property, write it to the cluster database (or delete
    it if it is zero length), and save it in the specified parameter block.

Arguments:

    hXsaction - Transaction handle.

    hkey - The opened cluster database key where the property is to be written.
        If not specified, the property will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyItem - The property from a property table to set/validate.

    rrvnModifiedNames - If the name of the property contains a backslash
        this object contains the modified name and keyname.

    pInBinaryValue - The value from the property list to set/validate.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameter block in which to return the data.  If specified,
        parameters will only be written if they are different between
        the input data and the parameter block, unless bForceWrite == TRUE.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - The format of the data is invalid for a property
        list value.

    A Win32 Error on failure.

--*/

{
    DWORD               status = ERROR_SUCCESS;
    BOOL                bZeroLengthData;
    PBYTE UNALIGNED *   ppbValue;
    DWORD *             pdwValue;

    // Loop to avoid goto's.
    do
    {
        bZeroLengthData = ( pInBinaryValue->cbLength == 0 );

        ppbValue = (PBYTE UNALIGNED *) &pOutParams[pPropertyItem->Offset];
        pdwValue = (DWORD *) &pOutParams[pPropertyItem->Offset + sizeof(PBYTE *)];

        //
        // If the data changed, write it and save it.
        // Do this even if only the case of the data changed.
        //
        if (    (pOutParams == NULL)
            ||  (*ppbValue == NULL)
            ||  (*pdwValue != pInBinaryValue->cbLength)
            ||  bZeroLengthData
            ||  bForceWrite
            ||  (memcmp( *ppbValue, pInBinaryValue->rgb, *pdwValue ) != 0) ) {

            //
            // Write the value to the cluster database.
            // If the data length is zero, delete the value.
            //
            if ( hkey != NULL ) {
                if ( bZeroLengthData ) {
                    if ( hXsaction ) {
                        status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                        hXsaction,
                                        hkey,
                                        rrvnModifiedNames.PszName() );
                    } else {
                        status = (*pClusterRegApis->pfnDeleteValue)(
                                        hkey,
                                        rrvnModifiedNames.PszName() );
                    } // if/else:  doing/not doing a transaction

                    //
                    // If the property doesn't exist in the
                    // cluster database, fix the status.
                    //
                    if ( status == ERROR_FILE_NOT_FOUND ) {
                        status = ERROR_SUCCESS;
                    } // if:  property already doesn't exist
                } else {
                    if ( hXsaction ) {
                        status = (*pClusterRegApis->pfnLocalSetValue)(
                                        hXsaction,
                                        hkey,
                                        rrvnModifiedNames.PszName(),
                                        REG_BINARY,
                                        (CONST BYTE *) &pInBinaryValue->rgb,
                                        pInBinaryValue->cbLength );
                    } else {
                        status = (*pClusterRegApis->pfnSetValue)(
                                        hkey,
                                        rrvnModifiedNames.PszName(),
                                        REG_BINARY,
                                        (CONST BYTE *) &pInBinaryValue->rgb,
                                        pInBinaryValue->cbLength );
                    } // if/else:  doing/not doing a transaction
                } // if/else:  zero length data
            } // if:  writing data

            //
            // Save the value to the output Parameter block.
            // If the data length is zero, set to the default.
            //
            if (    (status == ERROR_SUCCESS)
                &&  (pOutParams != NULL) ) {

                if ( *ppbValue != NULL ) {
                    LocalFree( *ppbValue );
                } // if:  previous value in parameter block

                if ( bZeroLengthData ) {
                    // If a default is specified, copy it.
                    if ( pPropertyItem->lpDefault != NULL ) {
                        *ppbValue = (LPBYTE) LocalAlloc( LMEM_FIXED, pPropertyItem->Minimum );
                        if ( *ppbValue == NULL ) {
                            status = GetLastError();
                            *pdwValue = 0;
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetBinaryProperty: error allocating memory for default BINARY value in parameter block for property '%1!ls!'.\n", rrvnModifiedNames.PszName() );
                            break;
                        } // if:  error allocating memory
                        CopyMemory( *ppbValue, pPropertyItem->lpDefault, pPropertyItem->Minimum );
                        *pdwValue = pPropertyItem->Minimum;
                    } else {
                        *ppbValue = NULL;
                        *pdwValue = 0;
                    } // if/else:  default value specified
                } else {
                    *ppbValue = (LPBYTE) LocalAlloc( LMEM_FIXED, pInBinaryValue->cbLength );
                    if ( *ppbValue == NULL ) {
                        status = GetLastError();
                        *pdwValue = 0;
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetBinaryProperty: error allocating memory for BINARY value in parameter block for property '%1!ls!'.\n", rrvnModifiedNames.PszName() );
                        break;
                    } // if:  error allocating memory
                    CopyMemory( *ppbValue, pInBinaryValue->rgb, pInBinaryValue->cbLength );
                    *pdwValue = pInBinaryValue->cbLength;
                } // if/else:  zero length data
            } // if:  data written successfully and parameter block specified
        } // if:  value changed or zero-length value
    } while ( 0 );

    return status;

} // ClRtlpSetBinaryProperty



DWORD
WINAPI
ClRtlpSetNonPropertyTable(
    IN HANDLE hXsaction,
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    IN PVOID Reserved,
    IN const PVOID pInPropertyList,
    IN DWORD cbInPropertyListSize
    )

/*++

Routine Description:

    Set items that are not in the property table list.

Arguments:

    hXsaction - Local Transaction handle.

    hkeyClusterKey - The opened registry key for this object's parameters.
        If not specified, the property list will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    pInPropertyList - The input buffer.

    cbInPropertyListSize - The input buffer size.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 Error on failure.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    PRESUTIL_PROPERTY_ITEM  propertyItem;
    DWORD                   inBufferSize;
    DWORD                   itemCount;
    DWORD                   dataSize;
    CLUSPROP_BUFFER_HELPER  buf;
    PCLUSPROP_PROPERTY_NAME pName;
    BOOL                    bZeroLengthData;
    CRegistryValueName      rvn;

    //
    // If hKeyClusterKey is present then 'normal' functions must be present.
    //
    if ( ( (hkeyClusterKey != NULL) &&
           ((pClusterRegApis->pfnSetValue == NULL) ||
           (pClusterRegApis->pfnCreateKey == NULL) ||
           (pClusterRegApis->pfnOpenKey == NULL) ||
           (pClusterRegApis->pfnCloseKey == NULL)
         )) ||
         ( pPropertyTable == NULL ) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: pClusterRegApis->pfnSetValue, pfnCreateKey, pfnOpenKey, or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // If hKeyClusterKey and hXsaction are present
    // then 'local' functions must be present.
    //
    if ( ((hkeyClusterKey != NULL) &&
           (hXsaction != NULL )) &&
           ((pClusterRegApis->pfnLocalCreateKey == NULL) ||
           (pClusterRegApis->pfnLocalDeleteValue == NULL)
         ) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: pClusterRegApis->pfnpfnLocalCreateKey or pfnLocalDeleteValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    if ( pInPropertyList == NULL ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: pInPropertyList == NULL. Returning ERROR_INVALID_DATA\n" );
        return(ERROR_INVALID_DATA);
    }

    buf.pb = (LPBYTE) pInPropertyList;
    inBufferSize = cbInPropertyListSize;

    //
    // Get the number of items in this list
    //
    if ( inBufferSize < sizeof(DWORD) ) {
        return(ERROR_INSUFFICIENT_BUFFER);
    }

    itemCount = buf.pList->nPropertyCount;
    buf.pdw++;

    //
    // Parse the rest of the items in the buffer.
    //
    while ( itemCount-- ) {
        //
        // Verify that the buffer is big enough to contain the
        // property name and a value.
        //
        pName = buf.pName;
        if ( inBufferSize < sizeof(*pName) ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }
        dataSize = sizeof(*pName) + ALIGN_CLUSPROP( pName->cbLength );
        if ( inBufferSize < dataSize + sizeof(CLUSPROP_VALUE) ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }

        //
        // Verify that the syntax of the property name is correct.
        //
        if ( pName->Syntax.dw != CLUSPROP_SYNTAX_NAME ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: syntax %1!d! not a name syntax.\n", pName->Syntax.dw );
            return(ERROR_INVALID_PARAMETER);
        }

        //
        // Verify that the length is correct for the string.
        //
        if ( pName->cbLength != (lstrlenW( pName->sz ) + 1) * sizeof(WCHAR) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: name is not a valid C string.\n" );
            return(ERROR_INVALID_DATA);
        }

        //
        // Move the buffer pointer to the property value.
        //
        buf.pb += dataSize;
        inBufferSize -= dataSize;

        //
        // Find the property name in the list of known properties.
        //
        propertyItem = pPropertyTable;
        while ( propertyItem->Name != NULL ) {

            if ( lstrcmpiW( pName->sz, propertyItem->Name ) == 0 ) {
                //
                // Verify that the buffer is big enough to contain the value.
                //
                do {
                    dataSize = sizeof(*buf.pValue)
                                + ALIGN_CLUSPROP( buf.pValue->cbLength );
                    if ( inBufferSize < dataSize ) {
                        return(ERROR_INSUFFICIENT_BUFFER);
                    }

                    //
                    // Skip this value.
                    //
                    buf.pb += dataSize;
                    inBufferSize -= dataSize;
                } while ( buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK );

                //
                // Skip the endmark.
                //
                dataSize = sizeof( CLUSPROP_SYNTAX );
                if ( inBufferSize < dataSize ) {
                    return(ERROR_INSUFFICIENT_BUFFER);
                }
                buf.pb += dataSize;
                inBufferSize -= dataSize;

                break;

            } else {
                propertyItem++;
            }
        }

        //
        // If no property name was found, just save this item.
        //
        if ( propertyItem->Name == NULL) {
            //
            // Verify that the buffer is big enough to contain the value.
            //
            dataSize = sizeof(*buf.pValue)
                        + ALIGN_CLUSPROP( buf.pValue->cbLength );
            if ( inBufferSize < dataSize + sizeof( CLUSPROP_SYNTAX ) ) {
                return(ERROR_INSUFFICIENT_BUFFER);
            }

            //
            // Verify that the syntax type is LIST_VALUE.
            //
            if ( buf.pSyntax->wType != CLUSPROP_TYPE_LIST_VALUE ) {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: Property '%1!ls!' type CLUSPROP_TYPE_LIST_VALUE (%2!d!) expected, was %3!d!.\n", pName->sz, CLUSPROP_TYPE_LIST_VALUE, buf.pSyntax->wType );
                return(ERROR_INVALID_PARAMETER);
            }

            //
            // If the value is not specified, delete the property.
            //
            bZeroLengthData = ( buf.pValue->cbLength == 0 );
            if ( bZeroLengthData ) {
                
                if ( hkeyClusterKey != NULL ) {
                    PVOID key = NULL;
                    DWORD disposition;

                    // 
                    // Use the wrapper class CRegistryValueName to parse value name to see if it 
                    // contains a backslash.
                    // 
                    status = rvn.ScInit( pName->sz, NULL );
                    if ( status != ERROR_SUCCESS ) {
                        break;
                    }

                    //
                    // If the value resides at a different location, open the key.
                    //
                    if ( rvn.PszKeyName() != NULL ) {
                        status = (*pClusterRegApis->pfnOpenKey)( hkeyClusterKey,
                                                                 rvn.PszKeyName(),
                                                                 KEY_ALL_ACCESS,
                                                                 &key);

                        if ( status != ERROR_SUCCESS ) {
                            break;
                        }
        
                    } else {
                        key = hkeyClusterKey;
                    }

                    if ( hXsaction != NULL ) {
                        status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                            hXsaction,
                                            key,
                                            rvn.PszName() );
                    }
                    else {
                        status = (*pClusterRegApis->pfnDeleteValue)(
                                            key,
                                            rvn.PszName() );
                    }

                    //
                    // If the property doesn't exist in the
                    // cluster database, fix the status.
                    //
                    if ( status == ERROR_FILE_NOT_FOUND ) {
                        status = ERROR_SUCCESS;
                    } // if:  property already doesn't exist

                    //
                    // Close the key if we opened it.
                    //
                    if ( (rvn.PszKeyName() != NULL) &&
                         (key != NULL) ) {
                        (*pClusterRegApis->pfnCloseKey)( key );
                    }

                } // if:  key specified
            } else {
                PVOID key = NULL;
                DWORD disposition;

                if ( hkeyClusterKey != NULL ) {
                    // 
                    // Use the wrapper class CRegistryValueName to parse value name to see if it 
                    // contains a backslash.
                    // 
                    status = rvn.ScInit( pName->sz, NULL );
                    if ( status != ERROR_SUCCESS ) {
                        break;
                    }

                    //
                    // If the value resides at a different location, open the key.
                    //
                    if ( rvn.PszKeyName() != NULL ) {

                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalCreateKey)( hXsaction,
                                                                            hkeyClusterKey,
                                                                            rvn.PszKeyName(),
                                                                            0,
                                                                            KEY_ALL_ACCESS,
                                                                            NULL,
                                                                            &key,
                                                                            &disposition);
                        }
                        else {
                            status = (*pClusterRegApis->pfnCreateKey)( hkeyClusterKey,
                                                                       rvn.PszKeyName(),
                                                                       0,
                                                                       KEY_ALL_ACCESS,
                                                                       NULL,
                                                                       &key,
                                                                       &disposition);
                        }

                        if ( status != ERROR_SUCCESS ) {
                            break;
                        }
        
                    } else {
                        key = hkeyClusterKey;
                    }
                }

                switch ( buf.pSyntax->wFormat ) {
                    case CLUSPROP_FORMAT_DWORD:
                        //
                        // Verify the length of the value.
                        //
                        if ( buf.pDwordValue->cbLength != sizeof(DWORD) ) {
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: Property '%1!ls!' length %2!d! not DWORD length.\n", pName->sz, buf.pDwordValue->cbLength );
                            status = ERROR_INVALID_DATA;
                            break;
                        }

                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction != NULL ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                               key,
                                                                               rvn.PszName(),
                                                                               REG_DWORD,
                                                                               (CONST BYTE*)&buf.pDwordValue->dw,
                                                                               sizeof(DWORD) );
                            }
                            else {
                                status = (*pClusterRegApis->pfnSetValue)( key,
                                                                          rvn.PszName(),
                                                                          REG_DWORD,
                                                                          (CONST BYTE*)&buf.pDwordValue->dw,
                                                                          sizeof(DWORD) );
                            }
                        }

                        break;

                    case CLUSPROP_FORMAT_LONG:
                        //
                        // Verify the length of the value.
                        //
                        if ( buf.pLongValue->cbLength != sizeof(LONG) ) {
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: Property '%1!ls!' length %2!d! not LONG length.\n", pName->sz, buf.pLongValue->cbLength );
                            status = ERROR_INVALID_DATA;
                            break;
                        }

                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction != NULL ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                               key,
                                                                               rvn.PszName(),
                                                                               REG_DWORD,
                                                                               (CONST BYTE*)&buf.pLongValue->l,
                                                                               sizeof(LONG) );
                            }
                            else {
                                status = (*pClusterRegApis->pfnSetValue)( key,
                                                                          rvn.PszName(),
                                                                          REG_DWORD,
                                                                          (CONST BYTE*)&buf.pLongValue->l,
                                                                          sizeof(LONG) );
                            }
                        }

                        break;

                    case CLUSPROP_FORMAT_ULARGE_INTEGER:
                    case CLUSPROP_FORMAT_LARGE_INTEGER:
                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)(
                                             hXsaction,
                                             key,
                                             rvn.PszName(),
                                             REG_QWORD,
                                             (CONST BYTE*)&buf.pULargeIntegerValue->li.QuadPart,
                                             sizeof(ULARGE_INTEGER));
                            } else {
                                status = (*pClusterRegApis->pfnSetValue)(
                                             key,
                                             rvn.PszName(),
                                             REG_QWORD,
                                             (CONST BYTE*)&buf.pULargeIntegerValue->li.QuadPart,
                                             sizeof(ULARGE_INTEGER));
                            }
                        }

                        break;

                    case CLUSPROP_FORMAT_SZ:
                        //
                        // Verify the length of the value.
                        //
                        if ( buf.pStringValue->cbLength != (lstrlenW( buf.pStringValue->sz ) + 1) * sizeof(WCHAR) ) {
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: Property '%1!ls!' length %2!d! doesn't match null-term. length.\n", pName->sz, buf.pStringValue->cbLength );
                            status = ERROR_INVALID_DATA;
                            break;
                        }

                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction != NULL ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                               key,
                                                                               rvn.PszName(),
                                                                               REG_SZ,
                                                                               (CONST BYTE*)buf.pStringValue->sz,
                                                                               buf.pStringValue->cbLength );
                            }
                            else {
                                status = (*pClusterRegApis->pfnSetValue)( key,
                                                                          rvn.PszName(),
                                                                          REG_SZ,
                                                                          (CONST BYTE*)buf.pStringValue->sz,
                                                                          buf.pStringValue->cbLength );
                            }
                        }

                        break;


                    case CLUSPROP_FORMAT_EXPAND_SZ:
                        //
                        // Verify the length of the value.
                        //
                        if ( buf.pStringValue->cbLength != (lstrlenW( buf.pStringValue->sz ) + 1) * sizeof(WCHAR) ) {
                            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: Property '%1!ls!' length %2!d! doesn't match null-term. length.\n", pName->sz, buf.pStringValue->cbLength );
                            status = ERROR_INVALID_DATA;
                            break;
                        }

                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction != NULL ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                               key,
                                                                               rvn.PszName(),
                                                                               REG_EXPAND_SZ,
                                                                               (CONST BYTE*)buf.pStringValue->sz,
                                                                               buf.pStringValue->cbLength );
                            }
                            else {
                                status = (*pClusterRegApis->pfnSetValue)( key,
                                                                          rvn.PszName(),
                                                                          REG_EXPAND_SZ,
                                                                          (CONST BYTE*)buf.pStringValue->sz,
                                                                          buf.pStringValue->cbLength );
                            }
                        }

                        break;

                    case CLUSPROP_FORMAT_MULTI_SZ:
                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction != NULL ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                               key,
                                                                               rvn.PszName(),
                                                                               REG_MULTI_SZ,
                                                                               (CONST BYTE*)buf.pStringValue->sz,
                                                                               buf.pStringValue->cbLength );
                            }
                            else {
                                status = (*pClusterRegApis->pfnSetValue)( key,
                                                                          rvn.PszName(),
                                                                          REG_MULTI_SZ,
                                                                          (CONST BYTE*)buf.pStringValue->sz,
                                                                          buf.pStringValue->cbLength );
                            }
                        }

                        break;

                    case CLUSPROP_FORMAT_BINARY:
                        //
                        // Write the value to the cluster database.
                        //
                        if ( key != NULL ) {
                            if ( hXsaction ) {
                                status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                               key,
                                                                               rvn.PszName(),
                                                                               REG_BINARY,
                                                                               (CONST BYTE*)buf.pBinaryValue->rgb,
                                                                               buf.pStringValue->cbLength );
                            } else {
                                status = (*pClusterRegApis->pfnSetValue)( key,
                                                                          rvn.PszName(),
                                                                          REG_BINARY,
                                                                          (CONST BYTE*)buf.pBinaryValue->rgb,
                                                                          buf.pStringValue->cbLength );
                            }
                        }

                        break;

                    default:
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetNonPropertyTable: Property '%1!ls!' unknown format %2!d! specified.\n", pName->sz, buf.pSyntax->wFormat );
                        status = ERROR_INVALID_PARAMETER;
                        break;

                } // switch

                //
                // Close the key if we opened it.
                //
                if ( (rvn.PszKeyName() != NULL) &&
                     (key != NULL) ) {
                    (*pClusterRegApis->pfnCloseKey)( key );
                }

            } // if/else:  zero length data

            //
            // Move the buffer past the value.
            //
            do {
                dataSize = sizeof(*buf.pValue)
                            + ALIGN_CLUSPROP( buf.pValue->cbLength );
                buf.pb += dataSize;
                inBufferSize -= dataSize;
            } while ( buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK );
            dataSize = sizeof( CLUSPROP_SYNTAX );
            buf.pb += dataSize;
            inBufferSize -= dataSize;
        }

        if ( status != ERROR_SUCCESS ) {
            break;
        }
    }

    return(status);

} // ClRtlpSetNonPropertyTable



DWORD
WINAPI
ClRtlSetPropertyParameterBlock(
    IN HANDLE hXsaction, 
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    IN PVOID Reserved,
    IN const LPBYTE pInParams,
    IN const PVOID pInPropertyList,
    IN DWORD cbInPropertyListSize,
    IN BOOL bForceWrite,
    IN OUT OPTIONAL LPBYTE pOutParams
    )

/*++

Routine Description:

Arguments:

    hXsaction - Transaction key used when called from the cluster service.

    hkeyClusterKey - The opened registry key for this object's parameters.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pPropertyTable - Pointer to the property table to process.

    pInParams - Parameter block to set.

    pInPropertyList - Full property list.

    cbInPropertyListSize - Size of the input full property list.

    bForceWrite - TRUE = always write the properties to the cluster database.
        FALSE = only write the properties if they changed.

    pOutParams - Parameters block to copy pInParams to.  If specified,
        parameters will only be written if they are different between
        the two parameter blocks.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 Error on failure.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    PRESUTIL_PROPERTY_ITEM  propertyItem;
    DWORD                   itemCount;
    DWORD                   dataSize;
    PVOID                   key;
    LPWSTR UNALIGNED *      ppszInValue;
    LPWSTR UNALIGNED *      ppszOutValue;
    PBYTE UNALIGNED *       ppbInValue;
    PBYTE UNALIGNED *       ppbOutValue;
    DWORD *                 pdwInValue;
    DWORD *                 pdwOutValue;
    ULARGE_INTEGER *        pullInValue;
    ULARGE_INTEGER *        pullOutValue;
    CRegistryValueName      rvn;

    //
    // If hKeyClusterKey is present then 'normal' functions must be present.
    //
    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis->pfnCreateKey == NULL) ||
         (pClusterRegApis->pfnSetValue == NULL) ||
         (pClusterRegApis->pfnCloseKey == NULL) ||
         (pPropertyTable == NULL) ||
         (pInParams == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlSetPropertyParameterBlock: hkeyClusterKey, pClusterRegApis->pfnCreateKey, pfnSetValue, or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // If hXsaction is present then 'local' functions must be present.
    //
    if ( (hXsaction != NULL ) &&
           ((pClusterRegApis->pfnLocalCreateKey == NULL) ||
           (pClusterRegApis->pfnLocalSetValue == NULL) ) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlSetPropertyParameterBlock: pClusterRegApis->pfnLocalCreateKey or pfnLocalDeleteValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    //
    // Parse the property table.
    //
    propertyItem = pPropertyTable;
    while ( propertyItem->Name != NULL ) {
        //
        // Make sure we are allowed to set this item.
        //
        if ( propertyItem->Flags & RESUTIL_PROPITEM_READ_ONLY ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlSetPropertyParameterBlock: Property '%1!ls!' is non-writable.\n", propertyItem->Name );
            return(ERROR_INVALID_PARAMETER);
        }

        // 
        // Use the wrapper class CRegistryValueName to parse value name to see if it 
        // contains a backslash.
        // 
        status = rvn.ScInit(  propertyItem->Name, propertyItem->KeyName );
        if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // If the value resides at a different location, create the key.
        //
        if ( rvn.PszKeyName() != NULL ) {

            DWORD disposition;

            if ( hXsaction != NULL ) {
                status = (*pClusterRegApis->pfnLocalCreateKey)( hXsaction,
                                                                hkeyClusterKey,
                                                                rvn.PszKeyName(),
                                                                0,
                                                                KEY_ALL_ACCESS,
                                                                NULL,
                                                                &key,
                                                                &disposition );
            } else {
                status = (*pClusterRegApis->pfnCreateKey)( hkeyClusterKey,
                                                           rvn.PszKeyName(),
                                                           0,
                                                           KEY_ALL_ACCESS,
                                                           NULL,
                                                           &key,
                                                           &disposition );
            }
            
            if ( status != ERROR_SUCCESS ) {
                return(status);
            }
        } else {
            key = hkeyClusterKey;
        }

        switch ( propertyItem->Format ) {
            case CLUSPROP_FORMAT_DWORD:
            case CLUSPROP_FORMAT_LONG:
                pdwInValue = (DWORD *) &pInParams[propertyItem->Offset];
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset];

                //
                // Write the value to the cluster database.
                //
                if ( hXsaction != NULL ) {
                    status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                   key,
                                                                   rvn.PszName(),
                                                                   REG_DWORD,
                                                                   (CONST BYTE*)pdwInValue,
                                                                   sizeof(DWORD) );
                } else {
                    status = (*pClusterRegApis->pfnSetValue)( key,
                                                              rvn.PszName(),
                                                              REG_DWORD,
                                                              (CONST BYTE*)pdwInValue,
                                                              sizeof(DWORD) );
                }

                //
                // Save the value to the output Parameter block.
                //
                if ( (status == ERROR_SUCCESS) &&
                     (pOutParams != NULL) ) {
                    *pdwOutValue = *pdwInValue;
                }
                break;

            case CLUSPROP_FORMAT_ULARGE_INTEGER:
            case CLUSPROP_FORMAT_LARGE_INTEGER:
                pullInValue = (ULARGE_INTEGER *) &pInParams[propertyItem->Offset];
                pullOutValue = (ULARGE_INTEGER *) &pOutParams[propertyItem->Offset];

                //
                // Write the value to the cluster database.
                //
                if ( hXsaction != NULL ) {
                    status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                   key,
                                                                   rvn.PszName(),
                                                                   REG_QWORD,
                                                                   (CONST BYTE*)pullInValue,
                                                                   sizeof(ULARGE_INTEGER) );
                } else {
                    status = (*pClusterRegApis->pfnSetValue)( key,
                                                              rvn.PszName(),
                                                              REG_QWORD,
                                                              (CONST BYTE*)pullInValue,
                                                              sizeof(ULARGE_INTEGER) );
                }

                //
                // Save the value to the output Parameter block.
                //
                if ( (status == ERROR_SUCCESS) && (pOutParams != NULL) ) {
                    pullOutValue->u = pullInValue->u;
                }
                break;

            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                ppszInValue = (LPWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];

                //
                // If the data changed, write it and save it.
                // Do this even if only the case of the data changed.
                //
                if ( bForceWrite ||
                     (pOutParams == NULL) ||
                     (*ppszOutValue == NULL) ||
                     (lstrcmpW( *ppszInValue, *ppszOutValue ) != 0) ) {

                    //
                    // Write the value to the cluster database.
                    //
                    if ( *ppszInValue != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           (propertyItem->Format == CLUSPROP_FORMAT_EXPAND_SZ
                                                                                 ? REG_EXPAND_SZ
                                                                                 : REG_SZ),
                                                                           (CONST BYTE*)*ppszInValue,
                                                                           (lstrlenW(*ppszInValue) + 1) * sizeof(WCHAR) );
                        } else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      (propertyItem->Format == CLUSPROP_FORMAT_EXPAND_SZ
                                                                            ? REG_EXPAND_SZ
                                                                            : REG_SZ),
                                                                      (CONST BYTE*)*ppszInValue,
                                                                      (lstrlenW(*ppszInValue) + 1) * sizeof(WCHAR) );
                        }
                    }

                    //
                    // Save the value to the output Parameter block.
                    //
                    if ( (status == ERROR_SUCCESS) &&
                         (pOutParams != NULL) ) {
                        if ( *ppszOutValue != NULL ) {
                            LocalFree( *ppszOutValue );
                        }
                        if ( *ppszInValue == NULL ) {
                            *ppszOutValue = NULL;
                        } else {
                            *ppszOutValue = (LPWSTR) LocalAlloc( LMEM_FIXED, (lstrlenW( *ppszInValue )+1) * sizeof(WCHAR) );
                            if ( *ppszOutValue == NULL ) {
                                status = GetLastError();
                                ClRtlDbgPrint(
                                    LOG_CRITICAL,
                                    "ClRtlSetPropertyParameterBlock: error allocating memory for "
                                    "SZ value '%1!ls!' in parameter block for property '%2!ls!'.\n",
                                    *ppszInValue,
                                    propertyItem->Name );
                                break;
                            }
                            lstrcpyW( *ppszOutValue, *ppszInValue );
                        }
                    }
                }
                break;

            case CLUSPROP_FORMAT_MULTI_SZ:
                ppszInValue = (LPWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                pdwInValue = (DWORD *) &pInParams[propertyItem->Offset+sizeof(LPWSTR*)];
                ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset+sizeof(LPWSTR*)];

                //
                // If the data changed, write it and save it.
                //
                if ( bForceWrite ||
                     (pOutParams == NULL) ||
                     (*ppszOutValue == NULL) ||
                     (*pdwInValue != *pdwOutValue) ||
                     (memcmp( *ppszInValue, *ppszOutValue, *pdwInValue ) != 0) ) {

                    //
                    // Write the value to the cluster database.
                    //
                    if ( *ppszInValue != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           REG_MULTI_SZ,
                                                                           (CONST BYTE*)*ppszInValue,
                                                                           *pdwInValue );
                        } else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      REG_MULTI_SZ,
                                                                      (CONST BYTE*)*ppszInValue,
                                                                      *pdwInValue );
                        }
                    }

                    //
                    // Save the value to the output Parameter block.
                    //
                    if ( (status == ERROR_SUCCESS) &&
                         (pOutParams != NULL) ) {
                        if ( *ppszOutValue != NULL ) {
                            LocalFree( *ppszOutValue );
                        }
                        if ( *ppszInValue == NULL ) {
                            *ppszOutValue = NULL;
                        } else {
                            *ppszOutValue = (LPWSTR) LocalAlloc( LMEM_FIXED, *pdwInValue );
                            if ( *ppszOutValue == NULL ) {
                                status = GetLastError();
                                *pdwOutValue = 0;
                                ClRtlDbgPrint(
                                    LOG_CRITICAL,
                                    "ClRtlSetPropertyParameterBlock: error allocating memory for "
                                    "MULTI_SZ value in parameter block for property '%1!ls!'.\n",
                                    propertyItem->Name );
                                break;
                            }
                            CopyMemory( *ppszOutValue, *ppszInValue, *pdwInValue );
                            *pdwOutValue = *pdwInValue;
                        }
                    }
                }
                break;

            case CLUSPROP_FORMAT_BINARY:
                ppbInValue = (PBYTE UNALIGNED *) &pInParams[propertyItem->Offset];
                pdwInValue = (DWORD *) &pInParams[propertyItem->Offset+sizeof(LPWSTR*)];
                ppbOutValue = (PBYTE UNALIGNED *) &pOutParams[propertyItem->Offset];
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset+sizeof(PBYTE*)];

                //
                // If the data changed, write it and save it.
                //
                if ( bForceWrite ||
                     (pOutParams == NULL) ||
                     (*ppbOutValue == NULL) ||
                     (*pdwInValue != *pdwOutValue) ||
                     (memcmp( *ppbInValue, *ppbOutValue, *pdwInValue ) != 0) ) {

                    //
                    // Write the value to the cluster database.
                    //
                    if ( *ppbInValue != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           REG_BINARY,
                                                                           (CONST BYTE*)*ppbInValue,
                                                                           *pdwInValue );
                        }
                        else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      REG_BINARY,
                                                                      (CONST BYTE*)*ppbInValue,
                                                                      *pdwInValue );
                        }
                    }

                    //
                    // Save the value to the output Parameter block.
                    //
                    if ( (status == ERROR_SUCCESS) &&
                         (pOutParams != NULL) ) {
                        if ( *ppbOutValue != NULL ) {
                            LocalFree( *ppbOutValue );
                        }
                        if ( *ppbInValue == NULL ) {
                            *ppbOutValue = NULL;
                        } else {
                            *ppbOutValue = (LPBYTE) LocalAlloc( LMEM_FIXED, *pdwInValue );
                            if ( *ppbOutValue == NULL ) {
                                status = GetLastError();
                                *pdwOutValue = 0;
                                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlSetPropertyParameterBlock: error allocating memory for BINARY value in parameter block for property '%1!ls!'.\n", propertyItem->Name );
                                break;
                            }
                            CopyMemory( *ppbOutValue, *ppbInValue, *pdwInValue );
                            *pdwOutValue = *pdwInValue;
                        }
                    }
                }
                break;

            default:
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlSetPropertyParameterBlock: Property '%1!ls!' unknown format %2!d! specified.\n", propertyItem->Name, propertyItem->Format );
                status = ERROR_INVALID_PARAMETER;
                break;

        }

        //
        // Close the key if we opened it.
        //
        if ( rvn.PszKeyName() != NULL ) {
            (*pClusterRegApis->pfnCloseKey)( key );
        }

        //
        // If an error occurred processing the property, cleanup and return.
        //
        if ( status != ERROR_SUCCESS ) {
            return(status);
        }

        propertyItem++;

    }

    //
    // Now find any parameters that are not represented in the property
    // table. All of these extra properties will just be set without validation.
    //
    if ( (status == ERROR_SUCCESS) &&
         (pInPropertyList != NULL) ) {
        status = ClRtlpSetNonPropertyTable( hXsaction,
                                            hkeyClusterKey,
                                            pClusterRegApis,
                                            pPropertyTable,
                                            NULL,
                                            pInPropertyList,
                                            cbInPropertyListSize );
    }

    return(status);

} // ClRtlSetPropertyParameterBlock



DWORD
WINAPI
ClRtlpSetPrivatePropertyList(
    IN HANDLE hXsaction,
    IN PVOID hkeyClusterKey,
    IN const PCLUSTER_REG_APIS pClusterRegApis,
    IN const PVOID pInPropertyList,
    IN DWORD cbInPropertyListSize
    )

/*++

Routine Description:

Arguments:

    hkeyClusterKey - The opened registry key for this resource's parameters.
        If not specified, the property list will only be validated.

    pClusterRegApis - Supplies a structure of function pointers for accessing
        the cluster database.

    pInPropertyList - The input buffer.

    cbInPropertyListSize - The input buffer size.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 Error on failure.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    DWORD                   inBufferSize;
    DWORD                   itemCount;
    DWORD                   dataSize;
    DWORD                   valueSize;
    CLUSPROP_BUFFER_HELPER  bufSizeTest;
    CLUSPROP_BUFFER_HELPER  buf;
    PCLUSPROP_PROPERTY_NAME pName;
    BOOL                    bZeroLengthData;
    CRegistryValueName      rvn;

    if ( (hkeyClusterKey != NULL) &&
         ( (pClusterRegApis->pfnSetValue == NULL) ||
           (pClusterRegApis->pfnCreateKey == NULL) ||
           (pClusterRegApis->pfnOpenKey == NULL) ||
           (pClusterRegApis->pfnCloseKey == NULL) ||
           (pClusterRegApis->pfnDeleteValue == NULL)
         ) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: pClusterRegApis->pfnCreateKey, pfnOpenKey, pfnSetValue, pfnCloseKey, or pfnDeleteValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }
    if ( pInPropertyList == NULL ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: pInPropertyList == NULL. Returning ERROR_INVALID_DATA\n" );
        return(ERROR_INVALID_DATA);
    }

    //
    // If hXsaction is present then 'local' functions must be present.
    //
    if ( (hXsaction != NULL ) &&
         ( (pClusterRegApis->pfnLocalCreateKey == NULL) ||
           (pClusterRegApis->pfnLocalDeleteValue == NULL)
         ) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: pClusterRegApis->pfnLocalCreateKey or pfnLocalDeleteValue == NULL. Returning ERROR_BAD_ARGUMENTS\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    buf.pb = (LPBYTE) pInPropertyList;
    inBufferSize = cbInPropertyListSize;

    //
    // Get the number of items in this list
    //
    if ( inBufferSize < sizeof(DWORD) ) {
        return(ERROR_INSUFFICIENT_BUFFER);
    }
    itemCount = buf.pList->nPropertyCount;
    buf.pdw++;

    //
    // Parse the rest of the items in the buffer.
    //
    while ( itemCount-- ) {
        pName = buf.pName;
        if ( inBufferSize < sizeof(*pName) ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }
        dataSize = sizeof(*pName) + ALIGN_CLUSPROP( pName->cbLength );
        if ( inBufferSize < dataSize + sizeof(CLUSPROP_VALUE) ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }

        //
        // Verify that the syntax of the property name is correct.
        //
        if ( pName->Syntax.dw != CLUSPROP_SYNTAX_NAME ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: syntax %1!d! not a name syntax.\n", pName->Syntax.dw );
            return(ERROR_INVALID_PARAMETER);
        }

        //
        // Verify that the length is correct for the string.
        //
        if ( pName->cbLength != (lstrlenW( pName->sz ) + 1) * sizeof(WCHAR) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "SetPrivatePropertyList: name is not a valid C string.\n" );
            return(ERROR_INVALID_DATA);
        }

        //
        // Move the buffer pointer to the property value.
        //
        buf.pb += dataSize;
        inBufferSize -= dataSize;

        //
        // Verify that the buffer is big enough to contain the value.
        //
        bufSizeTest.pb = buf.pb;
        dataSize = 0;
        do {
            valueSize = sizeof( *bufSizeTest.pValue )
                        + ALIGN_CLUSPROP( bufSizeTest.pValue->cbLength );
            bufSizeTest.pb += valueSize;
            dataSize += valueSize;
        } while ( bufSizeTest.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK );
        dataSize += sizeof( CLUSPROP_SYNTAX );
        if ( inBufferSize < dataSize ) {
            return(ERROR_INSUFFICIENT_BUFFER);
        }

        //
        // Verify that the syntax type is SPECIAL.
        //
        if ( buf.pSyntax->wType != CLUSPROP_TYPE_LIST_VALUE ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: Property '%1!ls!' type CLUSPROP_TYPE_LIST_VALUE (%2!d!) expected, was %3!d!.\n", pName->sz, CLUSPROP_TYPE_LIST_VALUE, buf.pSyntax->wType );
            return(ERROR_INVALID_PARAMETER);
        }

        //
        // If the value is not specified, delete the property.
        //
        bZeroLengthData = ( buf.pValue->cbLength == 0 );
        if ( bZeroLengthData ) {
            if ( hkeyClusterKey != NULL ) {
                PVOID key = NULL;

                // 
                // Use the wrapper class CRegistryValueName to parse value name to see if it 
                // contains a backslash.
                // 
                status = rvn.ScInit( pName->sz, NULL );
                if ( status != ERROR_SUCCESS ) {
                    break;
                }

                //
                // If the value resides at a different location, open the key.
                //
                if ( rvn.PszKeyName() != NULL ) {
                    status = (*pClusterRegApis->pfnOpenKey)( hkeyClusterKey,
                                                             rvn.PszKeyName(),
                                                             KEY_ALL_ACCESS,
                                                             &key);

                    if ( status != ERROR_SUCCESS ) {
                        break;
                    }
    
                } else {
                    key = hkeyClusterKey;
                }

                if ( hXsaction != NULL ) {
                    status = (*pClusterRegApis->pfnLocalDeleteValue)(
                                                hXsaction,
                                                key,
                                                rvn.PszName() );
                }
                else {
                    status = (*pClusterRegApis->pfnDeleteValue)(
                                                key,
                                                rvn.PszName() );
                }

                //
                // If the property doesn't exist in the
                // cluster database, fix the status.
                //
                if ( status == ERROR_FILE_NOT_FOUND ) {
                    status = ERROR_SUCCESS;
                } // if:  property already doesn't exist

                //
                // Close the key if we opened it.
                //
                if ( (rvn.PszKeyName() != NULL) &&
                     (key != NULL) ) {
                    (*pClusterRegApis->pfnCloseKey)( key );
                }

            } // if:  key specified
        } else {
            PVOID key = NULL;
            DWORD disposition;

            if ( hkeyClusterKey != NULL ) {
                // 
                // Use the wrapper class CRegistryValueName to parse value name to see if it 
                // contains a backslash.
                // 
                status = rvn.ScInit( pName->sz, NULL );
                if ( status != ERROR_SUCCESS ) {
                    break;
                }

                //
                // If the value resides at a different location, open the key.
                //
                if ( rvn.PszKeyName() != NULL ) {

                    if ( hXsaction != NULL )  {
                        status = (*pClusterRegApis->pfnLocalCreateKey)( 
                                                                   hXsaction,
                                                                   hkeyClusterKey,
                                                                   rvn.PszKeyName(),
                                                                   0,
                                                                   KEY_ALL_ACCESS,
                                                                   NULL,
                                                                   &key,
                                                                   &disposition);

                    }
                    else {
                        status = (*pClusterRegApis->pfnCreateKey)( hkeyClusterKey,
                                                                   rvn.PszKeyName(),
                                                                   0,
                                                                   KEY_ALL_ACCESS,
                                                                   NULL,
                                                                   &key,
                                                                   &disposition);
                    }

                    if ( status != ERROR_SUCCESS ) {
                        break;
                    }
    
                } else {
                    key = hkeyClusterKey;
                }
            }

            //
            // Parse the property and set it in the cluster database
            //
            switch ( buf.pSyntax->wFormat ) {
                case CLUSPROP_FORMAT_DWORD:
                case CLUSPROP_FORMAT_LONG:
                    //
                    // Verify the length of the value.
                    //
                    if ( buf.pDwordValue->cbLength != sizeof(DWORD) ) {
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: Property '%1!ls!' length %2!d! not DWORD or LONG length.\n", pName->sz, buf.pDwordValue->cbLength );
                        status = ERROR_INVALID_DATA;
                        break;
                    }

                    // 
                    // Write the value to the cluster database.
                    //
                    if ( key != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           REG_DWORD,
                                                                           (CONST BYTE*)&buf.pDwordValue->dw,
                                                                           sizeof(DWORD) );
                        }
                        else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      REG_DWORD,
                                                                      (CONST BYTE*)&buf.pDwordValue->dw,
                                                                      sizeof(DWORD));
                        }
                    }
                    break;

                case CLUSPROP_FORMAT_ULARGE_INTEGER:
                case CLUSPROP_FORMAT_LARGE_INTEGER:
                    //
                    // Verify the length of the value.
                    //
                    if ( buf.pULargeIntegerValue->cbLength != sizeof(ULARGE_INTEGER) ) {
                        ClRtlDbgPrint(LOG_CRITICAL,
                                      "ClRtlpSetPrivatePropertyList: Property '%1!ls!' length "
                                      "%2!d! not ULARGE_INTEGER or LARGE_INTEGER length.\n",
                                      pName->sz,
                                      buf.pULargeIntegerValue->cbLength );
                        status = ERROR_INVALID_DATA;
                        break;
                    }

                    // 
                    // Write the value to the cluster database.
                    //
                    if ( key != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)(
                                         hXsaction,
                                         key,
                                         rvn.PszName(),
                                         REG_QWORD,
                                         (CONST BYTE*)&buf.pULargeIntegerValue->li.QuadPart,
                                         sizeof(ULARGE_INTEGER) );
                        }
                        else {
                            status = (*pClusterRegApis->pfnSetValue)(
                                         key,
                                         rvn.PszName(),
                                         REG_QWORD,
                                         (CONST BYTE*)&buf.pULargeIntegerValue->li.QuadPart,
                                         sizeof(ULARGE_INTEGER));
                        }
                    }
                    break;

               case CLUSPROP_FORMAT_SZ:
               case CLUSPROP_FORMAT_EXPAND_SZ:
                    //
                    // Verify the length of the value.
                    //
                    if ( buf.pStringValue->cbLength != (lstrlenW( buf.pStringValue->sz ) + 1) * sizeof(WCHAR) ) {
                        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpSetPrivatePropertyList: Property '%1!ls!' length %2!d! doesn't match null-term. length.\n", pName->sz, buf.pStringValue->cbLength );
                        status = ERROR_INVALID_DATA;
                        break;
                    }

                    //
                    // Write the value to the cluster database.
                    //
                    if ( key != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           (buf.pSyntax->wFormat == CLUSPROP_FORMAT_EXPAND_SZ
                                                                                ? REG_EXPAND_SZ
                                                                                : REG_SZ),
                                                                           (CONST BYTE*)buf.pStringValue->sz,
                                                                           buf.pStringValue->cbLength);
                        }
                        else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      (buf.pSyntax->wFormat == CLUSPROP_FORMAT_EXPAND_SZ
                                                                            ? REG_EXPAND_SZ
                                                                            : REG_SZ),
                                                                      (CONST BYTE*)buf.pStringValue->sz,
                                                                      buf.pStringValue->cbLength);
                        }
                    }
                    break;

                case CLUSPROP_FORMAT_MULTI_SZ:
                    //
                    // Write the value to the cluster database.
                    //
                    if ( key != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           REG_MULTI_SZ,
                                                                           (CONST BYTE*)buf.pStringValue->sz,
                                                                           buf.pStringValue->cbLength );
                        }
                        else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      REG_MULTI_SZ,
                                                                      (CONST BYTE*)buf.pStringValue->sz,
                                                                      buf.pStringValue->cbLength );
                        }
                    }
                    break;

                case CLUSPROP_FORMAT_BINARY:
                    //
                    // Write the value to the cluster database.
                    //
                    if ( key != NULL ) {
                        if ( hXsaction != NULL ) {
                            status = (*pClusterRegApis->pfnLocalSetValue)( hXsaction,
                                                                           key,
                                                                           rvn.PszName(),
                                                                           REG_BINARY,
                                                                           (CONST BYTE*)buf.pBinaryValue->rgb,
                                                                           buf.pBinaryValue->cbLength );
                        }
                        else {
                            status = (*pClusterRegApis->pfnSetValue)( key,
                                                                      rvn.PszName(),
                                                                      REG_BINARY,
                                                                      (CONST BYTE*)buf.pBinaryValue->rgb,
                                                                      buf.pBinaryValue->cbLength );
                        }
                    }
                    break;

                default:
                    status = ERROR_INVALID_PARAMETER; // not tested

            } // switch

            //
            // Close the key if we opened it.
            //
            if ( (rvn.PszKeyName() != NULL) &&
                 (key != NULL) ) {
                (*pClusterRegApis->pfnCloseKey)( key );
            }

        } // if/else:  zero length data

        if ( status != ERROR_SUCCESS ) {
            break;
        }

        //
        // Move the buffer past the value.
        //
        buf.pb += dataSize;
        inBufferSize -= dataSize;

    }

    return(status);

} // ClRtlpSetPrivatePropertyList



DWORD
WINAPI
ClRtlpFindSzProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT LPWSTR * pszPropertyValue,
    IN BOOL bReturnExpandedValue
    )

/*++

Routine Description:

    Finds the specified string property in the Property List buffer pointed at
    by pPropertyList.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    pszPropertyValue - the matching string value found.

    bReturnExpandedValue - TRUE = return expanded value if one is present,
        FALSE = return the first value.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_FILE_NOT_FOUND - 

    ERROR_NOT_ENOUGH_MEMORY - 

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR                  valueData;
    LPWSTR                  listValueData;
    DWORD                   listByteLength;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            ((LONG)cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a string property.
            //
            if ( (props.pStringValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_SZ) &&
                 (props.pStringValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_EXPAND_SZ) ) {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpFindSzProperty: Property '%1!ls!' syntax (%2!d!, %3!d!) not proper list string syntax.\n", pszPropertyName, props.pSyntax->wType, props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it
            // and copy the value in.
            //
            if ( pszPropertyValue != NULL ) {
                //
                // If caller wants the expanded value, look at any
                // additional values in the value list to see if one
                // was returned.
                //
                listValueData = props.pStringValue->sz;
                listByteLength = props.pStringValue->cbLength;
                if ( bReturnExpandedValue ) {
                    //
                    // Skip past values in the value list looking for
                    // an expanded string value.
                    //
                    while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                            (cbPropertyListSize > 0) ) {
                        byteCount = sizeof(*props.pValue) + ALIGN_CLUSPROP(listByteLength);
                        cbPropertyListSize -= byteCount;
                        props.pb += byteCount;
                        if ( props.pSyntax->dw == CLUSPROP_SYNTAX_LIST_VALUE_EXPANDED_SZ ) {
                            listValueData = props.pStringValue->sz;
                            listByteLength = props.pStringValue->cbLength;
                            break;
                        }
                    }
                }

                //
                // Allocate a buffer for the string value and
                // copy the value from the property list.
                //
                valueData = (LPWSTR) LocalAlloc( LMEM_FIXED, listByteLength );
                if ( valueData == NULL ) {
                    return(ERROR_NOT_ENOUGH_MEMORY);
                }
                CopyMemory( valueData, listValueData, listByteLength );
                *pszPropertyValue = valueData;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list (one or more values + endmark).
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) ) {
                byteCount = sizeof(*props.pValue) + ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= byteCount;
                props.pb += byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlpFindSzProperty



DWORD
WINAPI
ClRtlFindDwordProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT LPDWORD pdwPropertyValue
    )

/*++

Routine Description:

    Finds the specified DWORD property in the Property List buffer pointed at
    by pPropertyList.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    pdwPropertyValue - the matching DWORD value found.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_FILE_NOT_FOUND - 

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR                  valueData;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            ((LONG)cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a dword property.
            //
            if ( props.pDwordValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_DWORD )  {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlFindDwordProperty: Property '%1!ls!' syntax (%2!d!, %3!d!) not proper list DWORD syntax.\n", pszPropertyName, props.pSyntax->wType, props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it.
            //
            if ( pdwPropertyValue ) {
                *pdwPropertyValue = props.pDwordValue->dw;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list and endmark.
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) ) {
                byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
                props.pb += sizeof(*props.pValue) + byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlFindDwordProperty

DWORD
WINAPI
ClRtlFindLongProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT LPLONG plPropertyValue
    )
/*++

Routine Description:

    Finds the specified dword in the Value List buffer pointed at by Buffer.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    plPropertyValue - the matching long value found.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_FILE_NOT_FOUND - 

    A Win32 error code on failure.

--*/
{
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR                  valueData;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            (cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a long property.
            //
            if ( props.pLongValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_DWORD )  {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlFindLongProperty: Property '%1!ls!' syntax (%2!d!, %3!d!) not proper list LONG syntax.\n", pszPropertyName, props.pSyntax->wType, props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it.
            //
            if ( plPropertyValue) {
                *plPropertyValue = props.pLongValue->l;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list and endmark.
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) ) {
                byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
                props.pb += sizeof(*props.pValue) + byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlFindLongProperty


DWORD
WINAPI
ClRtlFindULargeIntegerProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT PULARGE_INTEGER pullPropertyValue
    )

/*++

Routine Description:

    Finds the specified ULARGE_INTEGER property in the Property List buffer
    pointed at by pPropertyList.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    pullPropertyValue - the matching ULARGE_INTEGER value found.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_FILE_NOT_FOUND - 

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR                  valueData;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            ((LONG)cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a LARGE INT property.
            //
            if ( props.pULargeIntegerValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_LARGE_INTEGER )  {
                ClRtlDbgPrint(LOG_CRITICAL,
                              "ClRtlFindULargeIntegerProperty: Property '%1!ls!' syntax "
                              "(%2!d!, %3!d!) not proper list ULARGE_INTEGER syntax.\n",
                              pszPropertyName,
                              props.pSyntax->wType,
                              props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it.
            //
            if ( pullPropertyValue ) {
                pullPropertyValue->QuadPart = props.pULargeIntegerValue->li.QuadPart;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list and endmark.
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) )
            {
                byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
                props.pb += sizeof(*props.pValue) + byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlFindULargeIntegerProperty


DWORD
WINAPI
ClRtlFindLargeIntegerProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT PLARGE_INTEGER pllPropertyValue
    )

/*++

Routine Description:

    Finds the specified LARGE_INTEGER property in the Property List buffer
    pointed at by pPropertyList.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    pllPropertyValue - the matching ULARGE_INTEGER value found.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_FILE_NOT_FOUND - 

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR                  valueData;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            ((LONG)cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a large int property.
            //
            if ( props.pLargeIntegerValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_LARGE_INTEGER )  {
                ClRtlDbgPrint(LOG_CRITICAL,
                              "ClRtlFindLargeIntegerProperty: Property '%1!ls!' syntax "
                              "(%2!d!, %3!d!) not proper list ULARGE_INTEGER syntax.\n",
                              pszPropertyName,
                              props.pSyntax->wType,
                              props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it.
            //
            if ( pllPropertyValue ) {
                pllPropertyValue->QuadPart = props.pLargeIntegerValue->li.QuadPart;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list and endmark.
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) )
            {
                byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
                props.pb += sizeof(*props.pValue) + byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlFindLargeIntegerProperty


DWORD
WINAPI
ClRtlFindBinaryProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT LPBYTE * pbPropertyValue,
    OUT LPDWORD pcbPropertyValueSize
    )

/*++

Routine Description:

    Finds the specified binary property in the Property List buffer pointed at
    by pPropertyList.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    pbPropertyValue - the matching binary value found.

    pcbPropertyValueSize - the length of the matching binary value found.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_NOT_ENOUGH_MEMORY - 

    ERROR_FILE_NOT_FOUND - 

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER  props;
    PBYTE                   valueData;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            ((LONG)cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a binary property.
            //
            if ( props.pStringValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_BINARY ) {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpFindBinaryProperty: Property '%1!ls!' syntax (%2!d!, %3!d!) not proper list binary syntax.\n", pszPropertyName, props.pSyntax->wType, props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it.
            //
            if ( pbPropertyValue ) {
                valueData = (PBYTE) LocalAlloc( LMEM_FIXED, props.pBinaryValue->cbLength );
                if ( !valueData ) {
                    return(ERROR_NOT_ENOUGH_MEMORY);
                }
                CopyMemory( valueData, props.pBinaryValue->rgb, props.pBinaryValue->cbLength );
                *pbPropertyValue = valueData;
            }

            //
            // If caller wants the value size
            //
            if ( pcbPropertyValueSize ) {
                *pcbPropertyValueSize = props.pBinaryValue->cbLength;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list and endmark.
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) ) {
                byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
                props.pb += sizeof(*props.pValue) + byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlFindBinaryProperty



DWORD
WINAPI
ClRtlFindMultiSzProperty(
    IN const PVOID pPropertyList,
    IN DWORD cbPropertyListSize,
    IN LPCWSTR pszPropertyName,
    OUT LPWSTR * pszPropertyValue,
    OUT LPDWORD pcbPropertyValueSize
    )

/*++

Routine Description:

    Finds the specified multiple string property in the Property List buffer
    pointed at by pPropertyList.

Arguments:

    pPropertyList - a property list.

    cbPropertyListSize - the size in bytes of the data in pPropertyList.

    pszPropertyName - the property name to look for in the buffer.

    pszPropertyValue - the matching multiple string value found.

    pcbPropertyValueSize - the length of the matching multiple string value found.

Return Value:

    ERROR_SUCCESS if successful.

    ERROR_INVALID_DATA - 

    ERROR_NOT_ENOUGH_MEMORY - 

    ERROR_FILE_NOT_FOUND - 

    A Win32 error code on failure.

--*/

{
    CLUSPROP_BUFFER_HELPER  props;
    LPWSTR                  valueData;
    DWORD                   itemCount;
    DWORD                   byteCount;

    props.pb = (LPBYTE) pPropertyList;
    itemCount = *(props.pdw++);
    cbPropertyListSize -= sizeof(DWORD);

    while ( itemCount-- &&
            ((LONG)cbPropertyListSize > 0) ) {
        //
        // If we found the specified property, validate the entry and return
        // the value to the caller.
        //
        if ( (props.pName->Syntax.dw == CLUSPROP_SYNTAX_NAME) &&
             (lstrcmpiW( props.pName->sz, pszPropertyName ) == 0) ) {
            //
            // Calculate the size of the name and move to the value.
            //
            byteCount = ALIGN_CLUSPROP(props.pName->cbLength);
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Make sure this is a multi-sz property.
            //
            if ( props.pStringValue->Syntax.dw != CLUSPROP_SYNTAX_LIST_VALUE_MULTI_SZ ) {
                ClRtlDbgPrint( LOG_CRITICAL, "ClRtlpFindMultiSzProperty: Property '%1!ls!' syntax (%2!d!, %3!d!) not proper list MultiSz syntax.\n", pszPropertyName, props.pSyntax->wType, props.pSyntax->wFormat );
                return(ERROR_INVALID_DATA);
            }

            //
            // If caller wants the value, allocate a buffer for it.
            //
            if ( pszPropertyValue ) {
                valueData = (LPWSTR) LocalAlloc( LMEM_FIXED, props.pMultiSzValue->cbLength );
                if ( !valueData ) {
                    return(ERROR_NOT_ENOUGH_MEMORY);
                }
                CopyMemory( valueData, props.pBinaryValue->rgb, props.pMultiSzValue->cbLength );
                *pszPropertyValue = valueData;
            }

            //
            // If caller wants the value size
            //
            if ( pcbPropertyValueSize ) {
                *pcbPropertyValueSize = props.pMultiSzValue->cbLength;
            }

            //
            // We found the property so return success.
            //
            return(ERROR_SUCCESS);

        } else {
            //
            // Skip the name (value header + size of data).
            //
            byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
            cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
            props.pb += sizeof(*props.pValue) + byteCount;

            //
            // Skip it's value list and endmark.
            //
            while ( (props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK) &&
                    (cbPropertyListSize > 0) ) {
                byteCount = ALIGN_CLUSPROP(props.pValue->cbLength);
                cbPropertyListSize -= sizeof(*props.pValue) + byteCount;
                props.pb += sizeof(*props.pValue) + byteCount;
            }
            cbPropertyListSize -= sizeof(*props.pSyntax);
            props.pb += sizeof(*props.pSyntax);
        }
    }

    return(ERROR_FILE_NOT_FOUND);

} // ClRtlFindMultiSzProperty



DWORD
WINAPI
ClRtlGetBinaryValue(
    IN HKEY hkeyClusterKey,
    IN LPCWSTR pszValueName,
    OUT LPBYTE * ppbOutValue,
    OUT LPDWORD pcbOutValueSize,
    IN const PCLUSTER_REG_APIS pClusterRegApis
    )

/*++

Routine Description:

    Queries a REG_BINARY or REG_MULTI_SZ value out of the cluster
    database and allocates the necessary storage for it.

Arguments:

    hkeyClusterKey - Supplies the cluster key where the value is stored

    pszValueName - Supplies the name of the value.

    ppbOutValue - Supplies the address of a pointer in which to return the value.

    pcbOutValueSize - Supplies the address of a DWORD in which to return the
        size of the value.

    pfnQueryValue - Address of QueryValue function.

Return Value:

    ERROR_SUCCESS - The value was read successfully.

    ERROR_BAD_ARGUMENTS - 

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory for the value.

    Win32 error code - The operation failed.

--*/

{
    LPBYTE value = NULL;
    DWORD valueSize;
    DWORD valueType;
    DWORD status;

    PVOID key = NULL;
    CRegistryValueName rvn;

    //
    // Initialize the output parameters.
    //
    *ppbOutValue = NULL;
    *pcbOutValueSize = 0;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis == NULL) ||
         (pClusterRegApis->pfnQueryValue == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetBinaryValue: hkeyClusterKey, pClusterRegApis, or pClusterRegApis->pfnQueryValue == NULL. Returning ERROR_BAD_ARGUMENTS.\n" );
        return(ERROR_BAD_ARGUMENTS);
    }

    // 
    // Use the wrapper class CRegistryValueName to parse value name to see if it 
    // contains a backslash.
    // 
    status = rvn.ScInit( pszValueName, NULL );
    if ( status != ERROR_SUCCESS )
    {
       return status;
    }

    //
    // If the value resides at a different location, open the key.
    //
    if ( rvn.PszKeyName() != NULL ) {
        if ( (pClusterRegApis->pfnOpenKey == NULL) ||
             (pClusterRegApis->pfnCloseKey == NULL) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetBinaryValue: pClusterRegApis->pfnOpenKey or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS.\n" );
            return(ERROR_BAD_ARGUMENTS);
        }

        status = (*pClusterRegApis->pfnOpenKey)( hkeyClusterKey,
                                                 rvn.PszKeyName(),
                                                 KEY_READ,
                                                 &key);
        
    } else {
        key = hkeyClusterKey;
    }

    //
    // Dummy do-while loop to avoid gotos.
    //
    do
    {
        //
        // Get the size of the value so we know how much to allocate.
        //
        valueSize = 0;
        status = (*pClusterRegApis->pfnQueryValue)( key,
                                                    rvn.PszName(),
                                                    &valueType,
                                                    NULL,
                                                    &valueSize );
        if ( (status != ERROR_SUCCESS) &&
             (status != ERROR_MORE_DATA) ) {
            break;
        }

        //if the size is zero, just return
        if (valueSize == 0)
        {
            break;
        }
        //
        // Allocate a buffer to read the value into.
        //
        value = (LPBYTE) LocalAlloc( LMEM_FIXED, valueSize );
        if ( value == NULL ) {
            status = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        //
        // Read the value from the cluster database.
        //
        status = (*pClusterRegApis->pfnQueryValue)( key,
                                                    pszValueName,
                                                    &valueType,
                                                    (LPBYTE)value,
                                                    &valueSize );

        if ( status != ERROR_SUCCESS ) {
            LocalFree( value );
        } else {
            *ppbOutValue = value;
            *pcbOutValueSize = valueSize;
        }

    }
    while (FALSE);

    //
    // Close the key if we opened it.
    //
    if ( (rvn.PszKeyName() != NULL) &&
         (key != NULL) ) {
        (*pClusterRegApis->pfnCloseKey)( key );
    }

    return(status);

} // ClRtlGetBinaryValue



LPWSTR
WINAPI
ClRtlGetSzValue(
    IN HKEY hkeyClusterKey,
    IN LPCWSTR pszValueName,
    IN const PCLUSTER_REG_APIS pClusterRegApis
    )

/*++

Routine Description:

    Queries a REG_SZ or REG_EXPAND_SZ value out of the cluster database
    and allocates the necessary storage for it.

Arguments:

    hkeyClusterKey - Supplies the cluster key where the value is stored

    pszValueName - Supplies the name of the value.

    pfnQueryValue - Address of QueryValue function.

Return Value:

    A pointer to a buffer containing the value if successful.

    NULL if unsuccessful.  Call GetLastError() to get more details.

--*/

{
    PWSTR value;
    DWORD valueSize;
    DWORD valueType;
    DWORD status;
    PVOID key = NULL;
    CRegistryValueName rvn;

    if ( (hkeyClusterKey == NULL) ||
         (pClusterRegApis == NULL) ||
         (pClusterRegApis->pfnQueryValue == NULL) ) {
        ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetSzValue: hkeyClusterKey, pClusterRegApis, or pClusterRegApis->pfnQueryValue == NULL. Returning ERROR_BAD_ARGUMENTS.\n" );
        SetLastError(ERROR_BAD_ARGUMENTS);
        return(NULL);
    }

    // 
    // Use the wrapper class CRegistryValueName to parse value name to see if it 
    // contains a backslash.
    // 
    status = rvn.ScInit( pszValueName, NULL );
    if ( status != ERROR_SUCCESS )
    {
        SetLastError(status);
        return(NULL);
    }
    //
    // If the value resides at a different location, open the key.
    //
    if ( rvn.PszKeyName() != NULL ) {
        if ( (pClusterRegApis->pfnOpenKey == NULL) ||
             (pClusterRegApis->pfnCloseKey == NULL) ) {
            ClRtlDbgPrint( LOG_CRITICAL, "ClRtlGetSzValue: pClusterRegApis->pfnOpenKey or pfnCloseKey == NULL. Returning ERROR_BAD_ARGUMENTS.\n" );
            SetLastError(ERROR_BAD_ARGUMENTS);
            return(NULL);
        }

        status = (*pClusterRegApis->pfnOpenKey)( hkeyClusterKey,
                                                 rvn.PszKeyName(),
                                                 KEY_READ,
                                                 &key);
        
    } else {
        key = hkeyClusterKey;
    }

    //
    // Dummy do-while loop to avoid gotos.
    //
    do
    {
        //
        // Get the size of the value so we know how much to allocate.
        //
        valueSize = 0;
        status = (*pClusterRegApis->pfnQueryValue)( key,
                                                    rvn.PszName(),
                                                    &valueType,
                                                    NULL,
                                                    &valueSize );
        if ( (status != ERROR_SUCCESS) &&
             (status != ERROR_MORE_DATA) ) {
            SetLastError( status );
            value = NULL;
            break;
        }

        //
        // Add on the size of the null terminator.
        //
        valueSize += sizeof(UNICODE_NULL);

        //
        // Allocate a buffer to read the string into.
        //
        value = (PWSTR) LocalAlloc( LMEM_FIXED, valueSize );
        if ( value == NULL ) {
            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
            value = NULL;
            break;
        }

        //
        // Read the value from the cluster database.
        //
        status = (*pClusterRegApis->pfnQueryValue)( key,
                                                    rvn.PszName(),
                                                    &valueType,
                                                    (LPBYTE)value,
                                                    &valueSize );
        if ( status != ERROR_SUCCESS ) {
            LocalFree( value );
            value = NULL;
        } else if ( (valueType != REG_SZ) &&
                    (valueType != REG_EXPAND_SZ) &&
                    (valueType != REG_MULTI_SZ) ) {
            status = ERROR_INVALID_PARAMETER;
            LocalFree( value );
            SetLastError( status );
            value = NULL;
        }

    }
    while (FALSE);

    //
    // Close the key if we opened it.
    //
    if ( (rvn.PszKeyName() != NULL) &&
         (key != NULL) ) {
        (*pClusterRegApis->pfnCloseKey)( key );
    }

    return(value);

} // ClRtlGetSzValue



DWORD
WINAPI
ClRtlDupParameterBlock(
    OUT LPBYTE pOutParams,
    IN const LPBYTE pInParams,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable
    )

/*++

Routine Description:

    Deallocates any buffers allocated for a parameter block that are
    different than the buffers used for the input parameter block.

Arguments:

    pOutParams - Parameter block to return.

    pInParams - Reference parameter block.

    pPropertyTable - Pointer to the property table to process.

Return Value:

    ERROR_SUCCESS - Parameter block duplicated successfully.

--*/

{
    DWORD                   status = ERROR_SUCCESS;
    PRESUTIL_PROPERTY_ITEM  propertyItem = pPropertyTable;
    LPWSTR UNALIGNED *      ppszInValue;
    DWORD *                 pdwInValue;
    ULARGE_INTEGER *        pullInValue;
    LPWSTR UNALIGNED *      ppszOutValue;
    DWORD *                 pdwOutValue;
    ULARGE_INTEGER *        pullOutValue;

    while ( propertyItem->Name != NULL ) {
        switch ( propertyItem->Format ) {
            case CLUSPROP_FORMAT_DWORD:
            case CLUSPROP_FORMAT_LONG:
                pdwInValue = (DWORD *) &pInParams[propertyItem->Offset];
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset];
                *pdwOutValue = *pdwInValue;
                break;

            case CLUSPROP_FORMAT_ULARGE_INTEGER:
            case CLUSPROP_FORMAT_LARGE_INTEGER:
                pullInValue = (ULARGE_INTEGER *) &pInParams[propertyItem->Offset];
                pullOutValue = (ULARGE_INTEGER *) &pOutParams[propertyItem->Offset];
                pullOutValue->u = pullInValue->u;
                break;

            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
                ppszInValue = (LPWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];
                if ( *ppszInValue == NULL ) {
                    if ( propertyItem->lpDefault != NULL ) {
                        *ppszOutValue = (LPWSTR) LocalAlloc(
                                                     LMEM_FIXED,
                                                     (lstrlenW( (LPCWSTR) propertyItem->lpDefault ) + 1) * sizeof(WCHAR)
                                                     );
                        if ( *ppszOutValue == NULL ) {
                            status = GetLastError();
                        } else {
                            lstrcpyW( *ppszOutValue, (LPCWSTR) propertyItem->lpDefault );
                        }
                    } else {
                        *ppszOutValue = NULL;
                    }
                } else {
                    *ppszOutValue = (LPWSTR) LocalAlloc( LMEM_FIXED, (lstrlenW( *ppszInValue ) + 1) * sizeof(WCHAR) );
                    if ( *ppszOutValue == NULL ) {
                        status = GetLastError();
                    } else {
                        lstrcpyW( *ppszOutValue, *ppszInValue );
                    }
                }
                break;

            case CLUSPROP_FORMAT_MULTI_SZ:
            case CLUSPROP_FORMAT_BINARY:
                ppszInValue = (LPWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                pdwInValue = (DWORD *) &pInParams[propertyItem->Offset + sizeof(LPCWSTR)];
                ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];
                pdwOutValue = (DWORD *) &pOutParams[propertyItem->Offset + sizeof(LPWSTR)];
                if ( *ppszInValue == NULL ) {
                    if ( propertyItem->lpDefault != NULL ) {
                        *ppszOutValue = (LPWSTR) LocalAlloc( LMEM_FIXED, propertyItem->Minimum );
                        if ( *ppszOutValue == NULL ) {
                            status = GetLastError();
                            *pdwOutValue = 0;
                        } else {
                            *pdwOutValue = propertyItem->Minimum;
                            CopyMemory( *ppszOutValue, (const PVOID) propertyItem->lpDefault, *pdwOutValue );
                        }
                    } else {
                        *ppszOutValue = NULL;
                        *pdwOutValue = 0;
                    }
                } else {
                    *ppszOutValue = (LPWSTR) LocalAlloc( LMEM_FIXED, *pdwInValue );
                    if ( *ppszOutValue == NULL ) {
                        status = GetLastError();
                        *pdwOutValue = 0;
                    } else {
                        CopyMemory( *ppszOutValue, *ppszInValue, *pdwInValue );
                        *pdwOutValue = *pdwInValue;
                    }
                }
                break;
        }
        propertyItem++;
    }

    //
    // If an error occurred, make sure we don't leak memory.
    //
    if ( status != ERROR_SUCCESS ) {
        ClRtlFreeParameterBlock( pOutParams, pInParams, pPropertyTable );
    }

    return(status);

} // ClRtlDupParameterBlock



void
WINAPI
ClRtlFreeParameterBlock(
    IN OUT LPBYTE pOutParams,
    IN const LPBYTE pInParams,
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable
    )

/*++

Routine Description:

    Deallocates any buffers allocated for a parameter block that are
    different than the buffers used for the input parameter block.

Arguments:

    pOutParams - Parameter block to free.

    pInParams - Reference parameter block.

    pPropertyTable - Pointer to the property table to process.

Return Value:

    None.

--*/

{
    PRESUTIL_PROPERTY_ITEM  propertyItem = pPropertyTable;
    LPCWSTR UNALIGNED *     ppszInValue;
    LPWSTR UNALIGNED *      ppszOutValue;

    while ( propertyItem->Name != NULL ) {
        switch ( propertyItem->Format ) {
            case CLUSPROP_FORMAT_SZ:
            case CLUSPROP_FORMAT_EXPAND_SZ:
            case CLUSPROP_FORMAT_MULTI_SZ:
            case CLUSPROP_FORMAT_BINARY:
                ppszInValue = (LPCWSTR UNALIGNED *) &pInParams[propertyItem->Offset];
                ppszOutValue = (LPWSTR UNALIGNED *) &pOutParams[propertyItem->Offset];

                if ( (pInParams == NULL) || (*ppszInValue != *ppszOutValue) ) {
                    LocalFree( *ppszOutValue );
                }
                break;
        }
        propertyItem++;
    }

} // ClRtlFreeParameterBlock



DWORD
WINAPI
ClRtlMarshallPropertyTable(
    IN PRESUTIL_PROPERTY_ITEM    pPropertyTable,
    IN OUT  DWORD                dwSize,
    IN OUT  LPBYTE               pBuffer,
    OUT     DWORD                *Required
    )
/*++

Routine Description

    Rohit (rjain) : It marshalls the pPropertyTable into a buffer so that the
    buffer can be passed as an argument to the NmUpdatePerformFixups2 handler

    This function assumes that the value field in all the elements of the 
    table is 0.

Arguments
    pPropertyTable - This table is converted to buffer

    dwSize         - size in bytes of the pbuffer supplied 

    pbuffer        - byte array into which pPropertyTable is copied

    Required       - number of bytes required

Return Value
    returns 
        ERROR_SUCCESS on success, 
        ERROR_MORE_DATA if the size of pbuffer is insufficient

++*/
    
{
    DWORD                   dwPosition=sizeof(DWORD);
    PRESUTIL_PROPERTY_ITEM  pPropertyItem=pPropertyTable;
    BOOL                    copying = TRUE;
    DWORD                   items=0;
    DWORD                   dwNameLength;
    DWORD                   dwKeyLength;
    DWORD                   status=ERROR_SUCCESS;

    *Required=sizeof(DWORD); // first DWORD will contain the number of items in PropertyTable
    while(pPropertyItem->Name != NULL)
    {
        items++;
        dwNameLength=(lstrlenW(pPropertyItem->Name)+1)*sizeof(WCHAR);
        if(pPropertyItem->KeyName==NULL)
            dwKeyLength=0;
        else
            dwKeyLength=(lstrlenW(pPropertyItem->KeyName)+1)*sizeof(WCHAR);
        *Required+=(dwNameLength+dwKeyLength+8*sizeof(DWORD));

        // if pbufer is smaller than needed, copying is turned off
        // and only the required size is calculated
        if ((copying && (dwSize < *Required)))
            copying=FALSE;

        if(copying)
        {

            // copy length of name and then the name itself

            CopyMemory(pBuffer+dwPosition,&dwNameLength,sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));

            CopyMemory(pBuffer+dwPosition,pPropertyItem->Name,dwNameLength);
            dwPosition+=dwNameLength;

            //copy length of keyname and then the keyname itself

            CopyMemory(pBuffer+dwPosition,&dwKeyLength,sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));
            if(dwKeyLength!=0)
            {
                CopyMemory(pBuffer+dwPosition,pPropertyItem->KeyName,dwKeyLength);
                dwPosition+=dwKeyLength;
            }
            //now copy remaining fields
            CopyMemory(pBuffer+dwPosition,&(pPropertyItem->Format),sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));

            // ISSUE-2000/11/21-charlwi
            // this needs to be fixed for Large Int properties since they
            // don't store their values in Default, Minimum, and Maximum

            //IMP: the default value is always assumed to be a DWORD. This is
            // because the values for properties in PropertyTable are stored 
            // in a seperate parameter list. See ClRtlSetPropertyTable
            //
            CopyMemory(pBuffer+dwPosition,&(pPropertyItem->Default),sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));
        
            CopyMemory(pBuffer+dwPosition,&(pPropertyItem->Minimum),sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));

            CopyMemory(pBuffer+dwPosition,&(pPropertyItem->Maximum),sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));

            CopyMemory(pBuffer+dwPosition,&(pPropertyItem->Flags),sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));

            CopyMemory(pBuffer+dwPosition,&(pPropertyItem->Offset),sizeof(DWORD));
            dwPosition+=(sizeof(DWORD));
        }
        //
        // Advance to the next property.
        //
        pPropertyItem++;
    }
    if(copying)
    {
        CopyMemory(pBuffer,&items,sizeof(DWORD));
        status=ERROR_SUCCESS;
     }
    else
        status=ERROR_MORE_DATA;
    return status;

} // MarshallPropertyTable



DWORD
WINAPI
ClRtlUnmarshallPropertyTable(
    IN OUT PRESUTIL_PROPERTY_ITEM   *ppPropertyTable,
    IN LPBYTE                       pBuffer
    )

/*++

Routine Description
    Rohit (rjain) : It unmarshalls the pBuffer into a RESUTIL_PROPERTY_ITEM table
   
Arguments
    pPropertyTable - This is the resulting table

    pbuffer        - marshalled byte array 
Return Value
    returns 
        ERROR_SUCCESS on success, 
        Win32 error on error
++*/        
{
    PRESUTIL_PROPERTY_ITEM          propertyItem;
    DWORD                           items;
    DWORD                           dwPosition=sizeof(DWORD);
    DWORD                           dwLength;
    DWORD                           i;
    DWORD                           status=ERROR_SUCCESS;

    if((pBuffer==NULL) ||(ppPropertyTable==NULL))
    {
        ClRtlDbgPrint( LOG_CRITICAL, "[ClRtl] Uncopy PropertyTable: Bad Argumnets\r\n");
        return ERROR_BAD_ARGUMENTS;
    }

    CopyMemory(&items,pBuffer,sizeof(DWORD));
    *ppPropertyTable=(PRESUTIL_PROPERTY_ITEM)LocalAlloc(LMEM_FIXED,(items+1)*sizeof(RESUTIL_PROPERTY_ITEM));
    if(*ppPropertyTable == NULL)
    {
        status=GetLastError();
        goto FnExit;
    }
    propertyItem=*ppPropertyTable;
    for(i=0; i<items; i++)
    {

        CopyMemory(&dwLength,pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=sizeof(DWORD);
        propertyItem->Name = NULL;
        propertyItem->Name=(LPWSTR)LocalAlloc(LMEM_FIXED,dwLength);
        if(propertyItem->Name == NULL)
        {
            status=GetLastError();
            goto FnExit;
        }
        CopyMemory(propertyItem->Name,pBuffer+dwPosition,dwLength);
        dwPosition+=dwLength;

        CopyMemory(&dwLength,pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=sizeof(DWORD);
        propertyItem->KeyName=NULL;
        if (dwLength!=0)
        {
            propertyItem->KeyName=(LPWSTR)LocalAlloc(LMEM_FIXED,dwLength);
            if(propertyItem->KeyName == NULL)
            {
                status=GetLastError();
                goto FnExit;
            }
            CopyMemory(propertyItem->KeyName,pBuffer+dwPosition,dwLength);
            dwPosition+=dwLength;
        }
        //now rest of the fields - all DWORDS
        CopyMemory(&(propertyItem->Format),pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=(sizeof(DWORD));

        // ISSUE-2000/11/21-charlwi
        // this needs to be fixed for Large Int properties since they don't
        // store their values in Default, Minimum, and Maximum
        
        // IMP: the default value is always passed as a  DWORD. This is
        // because the values for properties in PropertyTable are stored 
        // in a seperate parameter list so the value here won't be used.
        // See ClRtlSetPropertyTable
        //

        CopyMemory(&(propertyItem->Default),pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=(sizeof(DWORD));
        
        CopyMemory(&(propertyItem->Minimum),pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=(sizeof(DWORD));

        CopyMemory(&(propertyItem->Maximum),pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=(sizeof(DWORD));

        CopyMemory(&(propertyItem->Flags),  pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=(sizeof(DWORD));

        CopyMemory(&(propertyItem->Offset), pBuffer+dwPosition,sizeof(DWORD));
        dwPosition+=(sizeof(DWORD));

        propertyItem++;
    }

   // the last entry is marked NULL to indicate the end of table
      propertyItem->Name=NULL;
    FnExit:
        return status;

} // UnmarshallPropertyTable



LPWSTR
WINAPI
ClRtlExpandEnvironmentStrings(
    IN LPCWSTR pszSrc
    )

/*++

Routine Description:

    Expands environment strings and returns an allocated buffer containing
    the result.

Arguments:

    pszSrc - Source string to expand.

Return Value:

    A pointer to a buffer containing the value if successful.

    NULL if unsuccessful.  Call GetLastError() to get more details.

--*/

{
    DWORD   status;
    DWORD   cchDst = 0;
    LPWSTR  pszDst = NULL;

    //
    // Get the required length of the output string.
    //
    cchDst = ExpandEnvironmentStrings( pszSrc, NULL, 0 );
    if ( cchDst == 0 ) {
        status = GetLastError();
    } else {
        //
        // Allocate a buffer for the expanded string.
        //
        pszDst = (LPWSTR) LocalAlloc( LMEM_FIXED, cchDst * sizeof(WCHAR) );
        if ( pszDst == NULL ) {
            status = ERROR_NOT_ENOUGH_MEMORY;
        } else {
            //
            // Get the expanded string.
            //
            cchDst = ExpandEnvironmentStrings( pszSrc, pszDst, cchDst );
            if ( cchDst == 0 ) {
                status = GetLastError();
                LocalFree( pszDst );
                pszDst = NULL;
            } else {
                status = ERROR_SUCCESS;
            }
        }
    }

    if ( status != ERROR_SUCCESS ) {
        SetLastError( status );
    }
    return(pszDst);

} // ClRtlExpandEnvironmentStrings



DWORD
WINAPI
ClRtlGetPropertyFormatSize(
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTableItem,
    IN OUT LPDWORD pcbOutPropertyListSize,
    IN OUT LPDWORD pnPropertyCount
    )

/*++

Routine Description:

    Get the total number of bytes required for this property item format.

Arguments:

    pPropertyTableItem - Supplies the property table item for the property
        format whose size is to be returned.

    pcbOutPropertyListSize - Supplies the size of the output buffer
        required to add this property to a property list.

    pnPropertyCount - The count of properties is incremented.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_INVALID_PARAMETER - 

    A Win32 error code on failure.

--*/

{
    WORD    formatType;
    DWORD   valueLength;
    DWORD   nameLength;
    PCLUSPROP_SYNTAX propSyntax;

    //
    // We will return a name, value pair.
    // each of which must be aligned.
    //
    // Get the format type.
    //propSyntax = (PCLUSPROP_SYNTAX) &pPropertyTableItem->Format;
    //formatType = propSyntax->wFormat;

    nameLength = sizeof(CLUSPROP_PROPERTY_NAME)
                    + ((wcslen( pPropertyTableItem->Name ) + 1) * sizeof(WCHAR))
                    + sizeof(CLUSPROP_SYNTAX); // for endmark

    nameLength = ALIGN_CLUSPROP( nameLength );
    valueLength = ALIGN_CLUSPROP( sizeof(CLUSPROP_WORD) );
    *pcbOutPropertyListSize += (valueLength + nameLength);
    *pnPropertyCount += 1;

    return(ERROR_SUCCESS);

} // ClRtlGetPropertyFormatSize



DWORD
WINAPI
ClRtlGetPropertyFormat(
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTableItem,
    IN OUT PVOID * pOutPropertyBuffer,
    IN OUT LPDWORD pcbOutPropertyBufferSize
    )

/*++

Routine Description:

    Get the total number of bytes required for this property item format.

Arguments:

    pPropertyTableItem - Supplies the property table item for the property
        format whose size is to be returned.

    pcbOutPropertyBuffer - Supplies the size of the output buffer
        required to add this property to a property list.

    pcbPropertyBufferSize - The size 

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_INVALID_PARAMETER - 

    A Win32 error code on failure.

--*/

{
    WORD    formatType;
    DWORD   valueLength;
    DWORD   nameLength;
    DWORD   bytesReturned;
    PCLUSPROP_SYNTAX propSyntax;
    CLUSPROP_BUFFER_HELPER  props;

    props.pb = (LPBYTE) *pOutPropertyBuffer;
    //
    // We will return a name, value pair.
    // each of which must be aligned.
    //
    // Get the format type.
    propSyntax = (PCLUSPROP_SYNTAX) &pPropertyTableItem->Format;
    formatType = propSyntax->wFormat;

    //
    // Copy the property name, which includes its syntax and length.
    //
    nameLength = (wcslen( pPropertyTableItem->Name ) + 1) * sizeof(WCHAR);
    props.pName->Syntax.dw = CLUSPROP_SYNTAX_NAME;
    props.pName->cbLength = nameLength;
    wcscpy( props.pName->sz, pPropertyTableItem->Name );
    bytesReturned = sizeof(*props.pName) + ALIGN_CLUSPROP( nameLength );
    *pcbOutPropertyBufferSize -= bytesReturned;
    props.pb += bytesReturned;

    //
    // Copy the property value, syntax, length, and ENDMARK
    //
    props.pWordValue->Syntax.wFormat = CLUSPROP_FORMAT_WORD;
    props.pWordValue->Syntax.wType = CLUSPROP_TYPE_LIST_VALUE;
    props.pName->cbLength = sizeof(WORD);
    props.pWordValue->w = formatType;
    bytesReturned = sizeof(*props.pWordValue) + sizeof(CLUSPROP_SYNTAX);
    props.pb += sizeof(*props.pWordValue);
    props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
    props.pb += sizeof(CLUSPROP_SYNTAX);
    bytesReturned = sizeof(*props.pWordValue) + sizeof(CLUSPROP_SYNTAX);
    *pcbOutPropertyBufferSize -= bytesReturned;

    *pOutPropertyBuffer = props.pb;

    return(ERROR_SUCCESS);

} // ClRtlGetPropertyFormat



DWORD
WINAPI
ClRtlGetPropertyFormats(
    IN const PRESUTIL_PROPERTY_ITEM pPropertyTable,
    OUT PVOID pOutPropertyFormatList,
    IN DWORD cbOutPropertyFormatListSize,
    OUT LPDWORD pcbBytesReturned,
    OUT LPDWORD pcbRequired
    )

/*++

Routine Description:

    Gets the 'known' property formats for a given object - given its
    property table.

Arguments:

    pPropertyTable - Pointer to the property table to process.

    pOutPropertyList - Supplies the output buffer.

    cbOutPropertyListSize - Supplies the size of the output buffer.

    pcbBytesReturned - The number of bytes returned in pOutPropertyList.

    pcbRequired - The required number of bytes if pOutPropertyList is too small.

Return Value:

    ERROR_SUCCESS - Operation was successful.

    ERROR_BAD_ARGUMENTS - An argument passed to the function was bad.

    ERROR_NOT_ENOUGH_MEMORY - Error allocating memory.

    A Win32 error code on failure.

--*/

{
    DWORD           status = ERROR_SUCCESS;
    DWORD           itemCount = 0;
    DWORD           totalBufferLength = 0;
    PVOID           outBuffer = pOutPropertyFormatList;
    DWORD           bufferLength = cbOutPropertyFormatListSize;
    PRESUTIL_PROPERTY_ITEM  property;

    *pcbBytesReturned = 0;
    *pcbRequired = 0;

    //
    // Clear the output buffer
    //
    if ( pOutPropertyFormatList != NULL ) {
        ZeroMemory( pOutPropertyFormatList, cbOutPropertyFormatListSize );
    }

    //
    // Get the size of all properties for this object.
    //
    property = pPropertyTable;
    while ( property->Name != NULL ) {
        status = ClRtlGetPropertyFormatSize( 
                                       property,
                                       &totalBufferLength,
                                       &itemCount );

        if ( status != ERROR_SUCCESS ) {
            break;
        }
        property++;
    }


    //
    // Continue only if the operations so far have been successful.
    //
    if ( status == ERROR_SUCCESS ) {
        //
        // Count for item count at front of return data and endmark.
        //
        totalBufferLength += sizeof(DWORD) + sizeof(CLUSPROP_SYNTAX);

        //
        // Verify the size of all the properties
        //
        if ( totalBufferLength > cbOutPropertyFormatListSize ) {
            *pcbRequired = totalBufferLength;
            totalBufferLength = 0;
            if ( pOutPropertyFormatList == NULL ) {
                status = ERROR_SUCCESS;
            } else {
                status = ERROR_MORE_DATA;
            }
        } else {
            *(LPDWORD)outBuffer = itemCount;
            outBuffer = (PVOID)( (PUCHAR)outBuffer + sizeof(itemCount) );
            bufferLength -= sizeof(itemCount);

            //
            // Now fetch all of the property Formats.
            //
            property = pPropertyTable;
            while ( property->Name != NULL ) {
                status = ClRtlGetPropertyFormat( 
                                           property,
                                           &outBuffer,
                                           &itemCount );

                if ( status != ERROR_SUCCESS ) {
                    break;
                }
                property++;
            }

            // Don't forget the ENDMARK
            *(LPDWORD)outBuffer = CLUSPROP_SYNTAX_ENDMARK;

            if ( (status != ERROR_SUCCESS) &&
                 (status != ERROR_MORE_DATA) ) {
                totalBufferLength = 0;
            }
        }

        *pcbBytesReturned = totalBufferLength;
    }

    return(status);

} // ClRtlGetPropertyFormats