|
|
//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1993
//
// File: propvar.cxx
//
// Contents: PROPVARIANT manipulation code
//
// History: 15-Aug-95 vich created
// 22-Feb-96 MikeHill Moved DwordRemain to "propmac.hxx".
// 09-May-96 MikeHill Use the 'boolVal' member of PropVariant
// rather than the member named 'bool'.
// 22-May-96 MikeHill Use the caller-provided codepage for
// string conversions, not the system default.
// 06-Jun-96 MikeHill Modify CLIPDATA.cbData to include sizeof
// ulClipFmt.
// 12-Jun-96 MikeHill - Use new BSTR alloc/free routines.
// - Added VT_I1 support (under ifdefs)
// - Bug for VT_CF|VT_VECTOR in RtlConvPropToVar
// 25-Jul-96 MikeHill - Removed Win32 SEH.
// - BSTRs: WCHAR=>OLECHAR
// - Added big-endian support.
// 10-Mar-98 MikeHill - Added support for Variant types except
// for VT_RECORD.
// 06-May-98 MikeHill - Removed usage of UnicodeCallouts.
// - Wrap SafeArray/BSTR calls for delayed-linking.
// - Enforce VT in VT_ARRAYs.
// - Added support for VT_VARIANT|VT_BYREF.
// - Added support for VT_ARRAY|VT_BYREF.
// - Added support for VT_VECTOR|VT_I1.
// - Use CoTaskMem rather than new/delete.
// 11-June-98 MikeHill - Validate elements of arrays & vectors.
//
//---------------------------------------------------------------------------
#include <pch.cxx>
#include <stdio.h>
#ifndef _MAC
#include <ddeml.h> // for CP_WINUNICODE
#endif
#include "propvar.h"
#ifndef newk
#define newk(Tag, pCounter) new
#endif
#if DBGPROP
BOOLEAN IsUnicodeString(WCHAR const *pwszname, ULONG cb) { return( TRUE ); }
BOOLEAN IsAnsiString(CHAR const *pszname, ULONG cb) { return( TRUE ); } #endif
//+---------------------------------------------------------------------------
// Function: PrpConvertToUnicode, private
//
// Synopsis: Convert a MultiByte string to a Unicode string
//
// Arguments: [pch] -- pointer to MultiByte string
// [cb] -- byte length of MultiByte string
// [CodePage] -- property set codepage
// [ppwc] -- pointer to returned pointer to Unicode string
// [pcb] -- returned byte length of Unicode string
//
// Returns: Nothing
//---------------------------------------------------------------------------
VOID PrpConvertToUnicode( IN CHAR const *pch, IN ULONG cb, IN USHORT CodePage, OUT WCHAR **ppwc, OUT ULONG *pcb, OUT NTSTATUS *pstatus) { WCHAR *pwszName;
*pstatus = STATUS_SUCCESS;
PROPASSERT(pch != NULL); PROPASSERT(ppwc != NULL); PROPASSERT(pcb != NULL);
*ppwc = NULL; *pcb = 0;
ULONG cwcName;
pwszName = NULL; cwcName = 0; while (TRUE) { cwcName = MultiByteToWideChar( CodePage, 0, // dwFlags
pch, cb, pwszName, cwcName); if (cwcName == 0) { CoTaskMemFree( pwszName ); *ppwc = NULL;
// If there was an error, assume that it was a code-page
// incompatibility problem.
StatusError(pstatus, "PrpConvertToUnicode: MultiByteToWideChar error", STATUS_UNMAPPABLE_CHARACTER); goto Exit; } if (pwszName != NULL) { DebugTrace(0, DEBTRACE_PROPERTY, ( "PrpConvertToUnicode: pch='%s'[%x] pwc='%ws'[%x->%x]\n", pch, cb, pwszName, *pcb, cwcName * sizeof(WCHAR))); break; } *pcb = cwcName * sizeof(WCHAR); *ppwc = pwszName = (WCHAR *) CoTaskMemAlloc( *pcb ); if (pwszName == NULL) { StatusNoMemory(pstatus, "PrpConvertToUnicode: no memory"); goto Exit; } }
// ----
// Exit
// ----
Exit:
return; }
//+---------------------------------------------------------------------------
// Function: PrpConvertToMultiByte, private
//
// Synopsis: Convert a Unicode string to a MultiByte string
//
// Arguments: [pwc] -- pointer to Unicode string
// [cb] -- byte length of Unicode string
// [CodePage] -- property set codepage
// [ppch] -- pointer to returned pointer to MultiByte string
// [pcb] -- returned byte length of MultiByte string
// [pstatus] -- pointer to NTSTATUS code
//
// Returns: Nothing
//---------------------------------------------------------------------------
VOID PrpConvertToMultiByte( IN WCHAR const *pwc, IN ULONG cb, IN USHORT CodePage, OUT CHAR **ppch, OUT ULONG *pcb, OUT NTSTATUS *pstatus) { ULONG cbName; CHAR *pszName;
*pstatus = STATUS_SUCCESS;
PROPASSERT(pwc != NULL); PROPASSERT(ppch != NULL); PROPASSERT(pcb != NULL);
*ppch = NULL; *pcb = 0;
// Ensure that cb is valid
if( 0 != (cb % 2) ) { StatusError(pstatus, "PrpConvertToMultiByte: Odd Unicode string cb", STATUS_INTERNAL_DB_CORRUPTION); goto Exit; }
pszName = NULL; cbName = 0; while (TRUE) { cbName = WideCharToMultiByte( CodePage, 0, // dwFlags
pwc, cb/sizeof(WCHAR), pszName, cbName, NULL, // lpDefaultChar
NULL); // lpUsedDefaultChar
if (cbName == 0) { CoTaskMemFree( pszName ); *ppch = NULL;
// If there was an error, assume that it was a code-page
// incompatibility problem.
StatusError(pstatus, "PrpConvertToMultiByte: WideCharToMultiByte error", STATUS_UNMAPPABLE_CHARACTER); goto Exit; } if (pszName != NULL) { DebugTrace(0, DEBTRACE_PROPERTY, ( "PrpConvertToMultiByte: pwc='%ws'[%x] pch='%s'[%x->%x]\n", pwc, cb, pszName, *pcb, cbName)); break; } *pcb = cbName; *ppch = pszName = reinterpret_cast<CHAR*>( CoTaskMemAlloc( cbName )); if (pszName == NULL) { StatusNoMemory(pstatus, "PrpConvertToMultiByte: no memory"); goto Exit; } }
// ----
// Exit
// ----
Exit:
return; }
//+---------------------------------------------------------------------------
//
// Function: SerializeSafeArrayBounds, private
//
// Synopsis: Write the rgsabounds field of a SAFEARRAY to pbdst (if non-NULL).
// Calculate and return the size of the serialized bounds,
// and the total number of elements in the array.
//
//---------------------------------------------------------------------------
NTSTATUS SerializeSafeArrayBounds( const SAFEARRAY *psa, BYTE *pbdst, ULONG *pcbBounds, ULONG *pcElems ) { NTSTATUS status = STATUS_SUCCESS;
ULONG ulIndex = 0; ULONG cDims = PrivSafeArrayGetDim( const_cast<SAFEARRAY*>(psa) ); PROPASSERT( 0 < cDims );
*pcbBounds = 0; *pcElems = 1;
// Loop through each dimension and get its range
for( ulIndex = 1; ulIndex <= cDims; ulIndex++ ) { LONG lLowerBound = 0, lUpperBound = 0;
// Get the lower & upper bounds
if( SUCCEEDED( status = PrivSafeArrayGetLBound( const_cast<SAFEARRAY*>(psa), ulIndex, &lLowerBound ))) { status = PrivSafeArrayGetUBound( const_cast<SAFEARRAY*>(psa), ulIndex, &lUpperBound ); } if( FAILED(status) ) { goto Exit; } else if( lUpperBound < lLowerBound ) { status = STATUS_INVALID_PARAMETER; goto Exit; }
// Update the total element count
*pcElems *= (lUpperBound - lLowerBound + 1 );
// If we're really serializing, write the current set of bounds
if( NULL != pbdst ) { // Write the length of this dimension
*(ULONG *) pbdst = (lUpperBound - lLowerBound + 1); pbdst += sizeof(ULONG);
// Then the lower bound
*(LONG *) pbdst = lLowerBound; pbdst += sizeof(LONG); } }
// Calculate the size of the rgsabound array.
*pcbBounds = sizeof(SAFEARRAYBOUND) * cDims;
Exit:
return( status ); }
ULONG CalcSafeArrayElements( ULONG cDims, const SAFEARRAYBOUND *rgsaBounds ) { ULONG cElems = 1; // Multiplicitive identity
for( ULONG i = 0; i < cDims; i++ ) cElems *= rgsaBounds[ i ].cElements;
return( cElems ); }
//+---------------------------------------------------------------------------
// Function: StgConvertVariantToProperty, private
//
// Synopsis: Convert a PROPVARIANT to a SERIALIZEDPROPERTYVALUE
//
// Arguments: [pvar] -- pointer to PROPVARIANT
// [CodePage] -- property set codepage
// [pprop] -- pointer to SERIALIZEDPROPERTYVALUE
// [pcb] -- pointer to remaining stream length,
// updated to actual property size on return
// [pid] -- propid (used if indirect)
// [fVariantVectorOrArray] -- TRUE if recursing on VT_VECTOR | VT_VARIANT
// [pcIndirect] -- pointer to indirect property count
// [pstatus] -- pointer to NTSTATUS code
//
// Returns: NULL if buffer too small, else input [pprop] argument
//---------------------------------------------------------------------------
// Define a macro which sets a variable named 'cbByteSwap', but
// only on big-endian builds. This value is not needed on little-
// endian builds (because byte-swapping is not necessary).
#ifdef BIGENDIAN
#define CBBYTESWAP(cb) cbByteSwap = cb
#elif LITTLEENDIAN
#define CBBYTESWAP(cb)
#else
#error Either BIGENDIAN or LITTLEENDIAN must be set.
#endif
// First, define a wrapper for this function which returns errors
// using NT Exception Handling, rather than returning an NTSTATUS.
#if defined(WINNT)
EXTERN_C SERIALIZEDPROPERTYVALUE * __stdcall StgConvertVariantToProperty( IN PROPVARIANT const *pvar, IN USHORT CodePage, OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, IN OUT ULONG *pcb, IN PROPID pid, IN BOOLEAN fVector, OPTIONAL OUT ULONG *pcIndirect) { SERIALIZEDPROPERTYVALUE *ppropRet; NTSTATUS status;
ppropRet = StgConvertVariantToPropertyNoEH( pvar, CodePage, pprop, pcb, pid, fVector, FALSE, // fArray
pcIndirect, NULL, &status );
if (!NT_SUCCESS( status )) RtlRaiseStatus( status );
return (ppropRet );
}
#endif // #if defined(WINNT)
// Enough for "prop%lu" + L'\0'
//#define CCH_MAX_INDIRECT_NAME (4 + 10 + 1)
// Now define the body of the function, returning errors with an
// NTSTATUS value instead of raising.
SERIALIZEDPROPERTYVALUE * StgConvertVariantToPropertyNoEH( IN PROPVARIANT const *pvar, IN USHORT CodePage, OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, IN OUT ULONG *pcb, IN PROPID pid, IN BOOLEAN fVector, // Used for recursive calls
IN BOOLEAN fArray, // Used for recursive calls
OPTIONAL OUT ULONG *pcIndirect, IN OUT OPTIONAL WORD *pwMinFormatRequired, OUT NTSTATUS *pstatus) { *pstatus = STATUS_SUCCESS;
// ------
// Locals
// ------
CHAR *pchConvert = NULL;
ULONG count = 0; BYTE *pbdst; ULONG cbch = 0; ULONG cbchdiv = 0; ULONG cb = 0; ULONG ulIndex = 0; // Used as a misc loop control variable
// Size of byte-swapping units (e.g. 2 to swap a WORD).
INT cbByteSwap = 0;
ULONG const *pcount = NULL; VOID const *pv = NULL; LONG *pclipfmt = NULL; BOOLEAN fCheckNullSource; BOOLEAN fIllegalType = FALSE; const VOID * const *ppv = NULL; OLECHAR aocName[ PROPGENPROPERTYNAME_SIZEOF ]; //CCH_MAX_INDIRECT_NAME ];
BOOLEAN fByRef;
const SAFEARRAY *parray = NULL; const VOID *parraydata = NULL; ULONG fSafeArrayLocked = FALSE; ULONG cSafeArrayDims = 0;
IFDBG( HRESULT &hr = *pstatus; ) propITraceStatic( "StgConvertVariantToPropertyNoEH" ); propTraceParameters(( "pprop=%p, CodePage=%d, pvar=%p, pma=%p" ));
// Initialize a local wMinFormatRequired - this is the minimum serialization
// format version required for the data in this property set (e.g., if you
// don't use any of the new NT5 support, you can stay a version 0 property set,
// otherwise you go to version 1).
WORD wMinFormatRequired = (NULL == pwMinFormatRequired) ? (WORD) PROPSET_WFORMAT_ORIGINAL : (WORD) *pwMinFormatRequired;
// If this is a byref, then up the min format required.
if( VT_BYREF & pvar->vt ) wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS );
// We dereference byrefs. If this is a byref Variant, we can shortcut this
// by simply changing pvar.
while( (VT_BYREF | VT_VARIANT) == pvar->vt ) { if( NULL == pvar->pvarVal ) { *pstatus = STATUS_INVALID_PARAMETER; goto Exit; } pvar = pvar->pvarVal; }
// Now that we've settled on the pvar we're going to convert,
// Jot down some info on it.
fCheckNullSource = (BOOLEAN) ((pvar->vt & VT_VECTOR) != 0); fByRef = 0 != (pvar->vt & VT_BYREF);
// If this is an array, then validate the VT in the SafeArray itself matches
// pvar->vt.
if( VT_ARRAY & pvar->vt ) { VARTYPE vtSafeArray = VT_EMPTY;
// It's invalid to have both the array and vector bits set (would it be
// an array of vectors or a vector of arrays?).
if( VT_VECTOR & pvar->vt ) { StatusInvalidParameter( pstatus, "Both VT_VECTOR and VT_ARRAY set" ); goto Exit; }
// Arrays require an uplevel property set format
wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS );
// Get the Type bit from the SafeArray
if( VT_BYREF & pvar->vt ) { if( NULL != pvar->pparray && NULL != *pvar->pparray ) { *pstatus = PrivSafeArrayGetVartype( *pvar->pparray, &vtSafeArray ); if( FAILED(*pstatus) ) goto Exit; } } else if( NULL != pvar->parray ) { *pstatus = PrivSafeArrayGetVartype( pvar->parray, &vtSafeArray ); if( FAILED(*pstatus) ) goto Exit; }
if( !NT_SUCCESS(*pstatus) ) goto Exit;
// Ensure the VT read from the property set matches that in the PropVariant.
// It is illegal for these to be different.
if( ( vtSafeArray & VT_TYPEMASK ) != ( pvar->vt & VT_TYPEMASK ) ) { *pstatus = STATUS_INVALID_PARAMETER; goto Exit; }
} // if( VT_ARRAY & pvar->vt )
// -------------------------------------------------------
// Analyze the PropVariant, and store information about it
// in fIllegalType, cb, pv, pcount, count, pclipfmt,
// fCheckNullSource, cbch, chchdiv, and ppv.
// -------------------------------------------------------
switch( pvar->vt ) { case VT_EMPTY: case VT_NULL: fIllegalType = fVector || fArray; break;
case VT_I1 | VT_BYREF: fIllegalType = fVector || fArray; case VT_I1: AssertByteField(cVal); // VT_I1
wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS ); cb = sizeof(pvar->bVal); pv = fByRef ? pvar->pcVal : &pvar->cVal; break;
case VT_UI1 | VT_BYREF: fIllegalType = fVector || fArray; case VT_UI1: AssertByteField(bVal); // VT_UI1
AssertStringField(pbVal);
cb = sizeof(pvar->bVal); pv = fByRef ? pvar->pbVal : &pvar->bVal; break;
case VT_I2 | VT_BYREF: case VT_UI2 | VT_BYREF: case VT_BOOL | VT_BYREF: fIllegalType = fVector || fArray; case VT_I2: case VT_UI2: case VT_BOOL:
AssertShortField(iVal); // VT_I2
AssertStringField(piVal); AssertShortField(uiVal); // VT_UI2
AssertStringField(puiVal); AssertShortField(boolVal); // VT_BOOL
cb = sizeof(pvar->iVal); pv = fByRef ? pvar->piVal : &pvar->iVal;
// If swapping, swap as a WORD
CBBYTESWAP(cb); break;
case VT_INT | VT_BYREF: case VT_UINT | VT_BYREF: fIllegalType = fVector || fArray; case VT_INT: case VT_UINT: fIllegalType |= fVector; // Fall through
case VT_I4 | VT_BYREF: case VT_UI4 | VT_BYREF: case VT_R4 | VT_BYREF: case VT_ERROR | VT_BYREF: fIllegalType = fVector || fArray; case VT_I4: case VT_UI4: case VT_R4: case VT_ERROR:
AssertLongField(lVal); // VT_I4
AssertStringField(plVal); AssertLongField(intVal); // VT_INT
AssertStringField(pintVal); AssertLongField(ulVal); // VT_UI4
AssertLongField(uintVal); // VT_UINT
AssertStringField(puintVal); AssertStringField(pulVal); AssertLongField(fltVal); // VT_R4
AssertStringField(pfltVal); AssertLongField(scode); // VT_ERROR
AssertStringField(pscode);
if( VT_INT == (pvar->vt&VT_TYPEMASK) || VT_UINT == (pvar->vt&VT_TYPEMASK) ) wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS );
cb = sizeof(pvar->lVal); pv = fByRef ? pvar->plVal : &pvar->lVal;
// If swapping, swap as a DWORD
CBBYTESWAP(cb); break;
case VT_FILETIME: fIllegalType = fArray;
/*
case VT_I8 | VT_BYREF: case VT_UI8 | VT_BYREF: fIllegalType = fVector || fArray; */
case VT_I8: case VT_UI8: AssertLongLongField(hVal); // VT_I8
AssertLongLongField(uhVal); // VT_UI8
AssertLongLongField(filetime); // VT_FILETIME
cb = sizeof(pvar->hVal); pv = &pvar->hVal;
// If swapping, swap each DWORD independently.
CBBYTESWAP(sizeof(DWORD)); break;
case VT_R8 | VT_BYREF: case VT_CY | VT_BYREF: case VT_DATE | VT_BYREF: fIllegalType = fVector || fArray; case VT_R8: case VT_CY: case VT_DATE:
AssertLongLongField(dblVal); // VT_R8
AssertStringField(pdblVal); AssertLongLongField(cyVal); // VT_CY
AssertStringField(pcyVal); AssertLongLongField(date); // VT_DATE
AssertStringField(pdate);
cb = sizeof(pvar->dblVal); pv = fByRef ? pvar->pdblVal : &pvar->dblVal;
// If swapping, swap as a LONGLONG (64 bits).
CBBYTESWAP(cb); break;
case VT_CLSID: AssertStringField(puuid); // VT_CLSID
fIllegalType = fArray; cb = sizeof(GUID); pv = pvar->puuid; fCheckNullSource = TRUE;
// If swapping, special handling is required.
CBBYTESWAP( CBBYTESWAP_UID ); break;
case VT_DECIMAL | VT_BYREF: fIllegalType = fVector || fArray; case VT_DECIMAL: fIllegalType |= fVector; wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS );
cb = sizeof(DECIMAL); pv = fByRef ? pvar->pdecVal : &pvar->decVal; break;
case VT_CF:
fIllegalType = fArray;
// Validate the PropVariant
if (pvar->pclipdata == NULL || pvar->pclipdata->cbSize < sizeof(pvar->pclipdata->ulClipFmt) ) { StatusInvalidParameter(pstatus, "StgConvertVariantToProperty: pclipdata NULL"); goto Exit; }
// How many bytes should we copy?
cb = CBPCLIPDATA( *(pvar->pclipdata) );
// Identify the value for this property's count field.
// (which includes sizeof(ulClipFmt))
count = pvar->pclipdata->cbSize; pcount = &count;
// Identify the clipdata's format & data
pclipfmt = &pvar->pclipdata->ulClipFmt; pv = pvar->pclipdata->pClipData;
fCheckNullSource = TRUE;
// Note that no byte-swapping of 'pv' is necessary.
break;
case VT_BLOB: case VT_BLOB_OBJECT: fIllegalType = fVector || fArray; pcount = &pvar->blob.cbSize; cb = *pcount; pv = pvar->blob.pBlobData; fCheckNullSource = TRUE;
// Note that no byte-swapping of 'pv' is necessary.
break;
case VT_VERSIONED_STREAM:
wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_VERSTREAM ); // Fall through
case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT:
fIllegalType = fVector || fArray; if( fIllegalType ) break;
// Does the caller support indirect properties?
if (pcIndirect != NULL) { // Yes.
(*pcIndirect)++;
// For indirect properties, we don't write the value
// in 'pvar', we write a substitute value. That value is by
// convention (IPropertyStorage knows to use PROPGENPROPERTYNAME),
// so we don't have to pass the name back to the caller.
PROPGENPROPERTYNAME_CB(aocName, sizeof(aocName), pid); pv = aocName;
}
// Otherwise, the caller doesn't support indirect properties,
// so we'll take the value from pwszVal
else { PROPASSERT( pvar->pwszVal == NULL || IsUnicodeString(pvar->pwszVal, MAXULONG)); pv = pvar->pwszVal; }
count = 1; // default to forcing an error on NULL pointer
// Jump to the LPSTR/BSTR handling code, but skip the ansi check
goto noansicheck;
break;
case VT_BSTR | VT_BYREF: fIllegalType = fVector || fArray; count = 0; pv = *pvar->pbstrVal; goto noansicheck;
case VT_LPSTR: fIllegalType = fArray; PROPASSERT( pvar->pszVal == NULL || IsAnsiString(pvar->pszVal, MAXULONG)); // FALLTHROUGH
case VT_BSTR: count = 0; // allow NULL pointer
pv = pvar->pszVal; noansicheck: AssertStringField(pwszVal); // VT_STREAM, VT_STREAMED_OBJECT
AssertStringField(pwszVal); // VT_STORAGE, VT_STORED_OBJECT
AssertStringField(bstrVal); // VT_BSTR
AssertStringField(pbstrVal); AssertStringField(pszVal); // VT_LPSTR
AssertStringField(pVersionedStream); // VT_VERSIONED_STREAM
if( fIllegalType ) break;
// We have the string for an LPSTR, BSTR, or indirect
// property pointed to by 'pv'. Now we'll perform any
// Ansi/Unicode conversions and byte-swapping that's
// necessary (putting the result in 'pv').
if (pv == NULL) { fCheckNullSource = TRUE; }
else if (pvar->vt == VT_LPSTR) { count = (ULONG)strlen((char *) pv) + 1;
// If the propset is Unicode, convert the LPSTR to Unicode.
if (CodePage == CP_WINUNICODE) { // Convert to Unicode.
PROPASSERT(IsAnsiString((CHAR const *) pv, count)); PrpConvertToUnicode( (CHAR const *) pv, count, CP_ACP, // Variants are in the system codepage
(WCHAR **) &pchConvert, &count, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// 'pv' always has the ready-to-serialize string.
pv = pchConvert;
// This unicode string may require byte-swapping.
CBBYTESWAP( sizeof(WCHAR) ); } } // else if (pvar->vt == VT_LPSTR)
else { // If this is a BSTR, increment the count to include
// the string terminator.
if( (~VT_BYREF & pvar->vt) == VT_BSTR ) { count = BSTRLEN(pv); // (This looks at the count field, not wcslen)
// Verify that the input BSTR is terminated.
if( S_OK != StringCbLengthW((const OLECHAR*)pv, count+sizeof(OLECHAR), NULL )) { StatusInvalidParameter(pstatus, "StgConvertVariantToProperty: bad BSTR null char"); goto Exit; }
// Increment the count to include the terminator.
count += sizeof(OLECHAR); } else { count = ((ULONG)Prop_ocslen((OLECHAR *) pv) + 1) * sizeof(OLECHAR); PROPASSERT(IsOLECHARString((OLECHAR const *) pv, count)); }
// This string is either an indirect property name,
// or a BSTR, both of which could be Ansi or Unicode.
if (CodePage != CP_WINUNICODE // Ansi property set
&& OLECHAR_IS_UNICODE // The PropVariant is in Unicode
) { // A Unicode to Ansi conversion is required.
PROPASSERT( IsUnicodeString( (WCHAR*)pv, count ));
PrpConvertToMultiByte( (WCHAR const *) pv, count, CodePage, &pchConvert, &count, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; pv = pchConvert; }
else if (CodePage == CP_WINUNICODE // Unicode property set,
&& !OLECHAR_IS_UNICODE // The PropVariant is in Ansi
) { // An Ansi to Unicode conversion is required.
PROPASSERT(IsAnsiString((CHAR const *) pv, count)); PROPASSERT(sizeof(OLECHAR) == sizeof(CHAR));
PrpConvertToUnicode( (CHAR const *) pv, count, CP_ACP, // In-mem BSTR is in system CP
(WCHAR **) &pchConvert, &count, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// 'pv' always holds the ready-to-serialize value.
pv = pchConvert;
// This unicode string may require swapping.
CBBYTESWAP( sizeof(WCHAR) ); }
else if (CodePage == CP_WINUNICODE) { // No conversion is required (i.e., both 'pv' and the
// property set are Unicode). But we must remember
// to perform a byte-swap (if byte-swapping is necessary).
CBBYTESWAP( sizeof(WCHAR) ); } } // if (pv == NULL) ... else if ... else
// Validate 'pv'.
#ifdef LITTLEENDIAN
PROPASSERT( NULL == pv || CodePage == CP_WINUNICODE && IsUnicodeString((WCHAR*)pv, count) || CodePage != CP_WINUNICODE && IsAnsiString((CHAR*)pv, count) ); #endif
cb = count; pcount = &count; break;
case VT_LPWSTR: AssertStringField(pwszVal); // VT_LPWSTR
PROPASSERT( pvar->pwszVal == NULL || IsUnicodeString(pvar->pwszVal, MAXULONG));
fIllegalType = fArray; pv = pvar->pwszVal; if (pv == NULL) { count = 0; fCheckNullSource = TRUE; } else { // Calculate the [length] field.
count = (ULONG)Prop_wcslen(pvar->pwszVal) + 1;
// If byte-swapping will be necessary to get to the serialized
// format, we'll do so in units of WCHARs.
CBBYTESWAP( sizeof(WCHAR) ); }
cb = count * sizeof(WCHAR); pcount = &count; break;
/*
case VT_RECORD:
pv = pvar->pvRecord; pRecInfo = pvar->pRecInfo;
if( NULL == pv ) { count = 0; fCheckNullSource = TRUE; } else if( NULL == pRecInfo ) { StatusInvalidParameter( pstatus, "Missing IRecordInfo*" ); goto Exit; }
cb = 0;
break; */
// Vector properties:
case VT_VECTOR | VT_I1: AssertByteVector(cac); // VT_I1
fIllegalType = fArray; wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS ); // Fall through
case VT_VECTOR | VT_UI1: AssertByteVector(caub); // VT_UI1
fIllegalType = fArray; pcount = &pvar->caub.cElems; cb = *pcount * sizeof(pvar->caub.pElems[0]); pv = pvar->caub.pElems; break;
case VT_VECTOR | VT_I2: case VT_VECTOR | VT_UI2: case VT_VECTOR | VT_BOOL: AssertShortVector(cai); // VT_I2
AssertShortVector(caui); // VT_UI2
AssertShortVector(cabool); // VT_BOOL
fIllegalType = fArray; pcount = &pvar->cai.cElems; cb = *pcount * sizeof(pvar->cai.pElems[0]); pv = pvar->cai.pElems;
// If swapping, swap as WORDs
CBBYTESWAP(sizeof(pvar->cai.pElems[0])); break;
case VT_VECTOR | VT_I4: case VT_VECTOR | VT_UI4: case VT_VECTOR | VT_R4: case VT_VECTOR | VT_ERROR: AssertLongVector(cal); // VT_I4
AssertLongVector(caul); // VT_UI4
AssertLongVector(caflt); // VT_R4
AssertLongVector(cascode); // VT_ERROR
fIllegalType = fArray; pcount = &pvar->cal.cElems; cb = *pcount * sizeof(pvar->cal.pElems[0]); pv = pvar->cal.pElems;
// If swapping, swap as DWORDs
CBBYTESWAP(sizeof(pvar->cal.pElems[0])); break;
case VT_VECTOR | VT_I8: case VT_VECTOR | VT_UI8: case VT_VECTOR | VT_FILETIME: AssertLongLongVector(cah); // VT_I8
AssertLongLongVector(cauh); // VT_UI8
AssertLongLongVector(cafiletime);// VT_FILETIME
fIllegalType = fArray; pcount = &pvar->cah.cElems; cb = *pcount * sizeof(pvar->cah.pElems[0]); pv = pvar->cah.pElems;
// If swapping, swap as DWORDs
CBBYTESWAP(sizeof(DWORD)); break;
case VT_VECTOR | VT_R8: case VT_VECTOR | VT_CY: case VT_VECTOR | VT_DATE: AssertLongLongVector(cadbl); // VT_R8
AssertLongLongVector(cacy); // VT_CY
AssertLongLongVector(cadate); // VT_DATE
fIllegalType = fArray; pcount = &pvar->cah.cElems; cb = *pcount * sizeof(pvar->cadbl.pElems[0]); pv = pvar->cadbl.pElems;
// If swapping, swap as LONGLONGs (8 bytes)
CBBYTESWAP(sizeof(pvar->cadbl.pElems[0])); break;
case VT_VECTOR | VT_CLSID: AssertVarVector(cauuid, sizeof(GUID));
fIllegalType = fArray; pcount = &pvar->cauuid.cElems; cb = *pcount * sizeof(pvar->cauuid.pElems[0]); pv = pvar->cauuid.pElems;
// If swapping, special handling is required.
CBBYTESWAP( CBBYTESWAP_UID ); break;
case VT_VECTOR | VT_CF: fIllegalType = fArray; cbch = sizeof(CLIPDATA); cbchdiv = sizeof(BYTE); goto stringvector;
case VT_VECTOR | VT_BSTR: case VT_VECTOR | VT_LPSTR: fIllegalType = fArray; cbchdiv = cbch = sizeof(BYTE); goto stringvector;
case VT_VECTOR | VT_LPWSTR: fIllegalType = fArray; cbchdiv = cbch = sizeof(WCHAR); goto stringvector;
case VT_VECTOR | VT_VARIANT: fIllegalType = fArray; cbch = MAXULONG; stringvector: AssertVarVector(caclipdata, sizeof(CLIPDATA)); // VT_CF
AssertStringVector(cabstr); // VT_BSTR
AssertStringVector(calpstr); // VT_LPSTR
AssertStringVector(calpwstr); // VT_LPWSTR
AssertVarVector(capropvar, sizeof(PROPVARIANT));// VT_VARIANT
pcount = &pvar->calpstr.cElems; ppv = (VOID **) pvar->calpstr.pElems; break;
case VT_ARRAY | VT_BSTR: case VT_ARRAY | VT_BSTR | VT_BYREF: fIllegalType = fVector || fArray; cbchdiv = cbch = sizeof(BYTE); cb = 1; // Fall through
case VT_ARRAY | VT_VARIANT: case VT_ARRAY | VT_VARIANT | VT_BYREF: fIllegalType = fVector || fArray; if( 0 == cbch ) cbch = MAXULONG;
pcount = &count;
case VT_ARRAY | VT_I1: case VT_ARRAY | VT_I1 | VT_BYREF: case VT_ARRAY | VT_UI1: case VT_ARRAY | VT_UI1 | VT_BYREF: case VT_ARRAY | VT_I2: case VT_ARRAY | VT_I2 | VT_BYREF: case VT_ARRAY | VT_UI2: case VT_ARRAY | VT_UI2 | VT_BYREF: case VT_ARRAY | VT_BOOL: case VT_ARRAY | VT_BOOL | VT_BYREF: case VT_ARRAY | VT_I4: case VT_ARRAY | VT_I4 | VT_BYREF: case VT_ARRAY | VT_UI4: case VT_ARRAY | VT_UI4 | VT_BYREF: /*
case VT_ARRAY | VT_I8: case VT_ARRAY | VT_I8 | VT_BYREF: case VT_ARRAY | VT_UI8: case VT_ARRAY | VT_UI8 | VT_BYREF: */ case VT_ARRAY | VT_INT: case VT_ARRAY | VT_INT | VT_BYREF: case VT_ARRAY | VT_UINT: case VT_ARRAY | VT_UINT | VT_BYREF: case VT_ARRAY | VT_R4: case VT_ARRAY | VT_R4 | VT_BYREF: case VT_ARRAY | VT_ERROR: case VT_ARRAY | VT_ERROR | VT_BYREF: case VT_ARRAY | VT_DECIMAL: case VT_ARRAY | VT_DECIMAL | VT_BYREF: case VT_ARRAY | VT_R8: case VT_ARRAY | VT_R8 | VT_BYREF: case VT_ARRAY | VT_CY: case VT_ARRAY | VT_CY | VT_BYREF: case VT_ARRAY | VT_DATE: case VT_ARRAY | VT_DATE | VT_BYREF:
fIllegalType = fVector || fArray; if( fIllegalType ) break;
wMinFormatRequired = (WORD) max( wMinFormatRequired, PROPSET_WFORMAT_EXPANDED_VTS );
parray = (VT_BYREF & pvar->vt) ? *pvar->pparray : pvar->parray;
if( NULL == parray ) cb = 0; else { // Get a pointer to the raw data
*pstatus = PrivSafeArrayAccessData( const_cast<SAFEARRAY*>(parray), const_cast<void**>(&parraydata) ); if( FAILED(*pstatus) ) goto Exit; fSafeArrayLocked = TRUE;
pv = parraydata; ppv = static_cast<const void* const*>(pv);
// Determine the dimension count and element size
cSafeArrayDims = PrivSafeArrayGetDim( const_cast<SAFEARRAY*>(parray) ); cb = PrivSafeArrayGetElemsize( const_cast<SAFEARRAY*>(parray) ); PROPASSERT( 0 != cb );
if( 0 == cSafeArrayDims ) { StatusInvalidParameter( pstatus, "Zero-length safearray dimension" ); goto Exit; }
// Determine the number of elements, and the total size of parraydata
count = CalcSafeArrayElements( cSafeArrayDims, parray->rgsabound ); cb *= count; }
break;
default: propDbg(( DEB_IWARN, "StgConvertVariantToProperty: unsupported vt=%d\n", pvar->vt)); *pstatus = STATUS_NOT_SUPPORTED; goto Exit;
} // switch (pvar->vt)
// ---------------------------------------------------------
// Serialize the property into the property set (pprop->rgb)
// ---------------------------------------------------------
// At this point we've analyzed the PropVariant, and stored
// information about it in various local variables. Now we
// can use this information to serialize the propvar.
// Early exit if this is an illegal type.
if (fIllegalType) { propDbg(( DEB_ERROR, "vt=%d\n", pvar->vt )); StatusInvalidParameter(pstatus, "StgConvertVariantToProperty: Illegal VarType"); goto Exit; }
// Set pbdst to point into the serialization buffer, or to
// NULL if there is no such buffer.
if (pprop == NULL) { pbdst = NULL; } else { pbdst = pprop->rgb; }
// Is this an Array/Vector of Strings/Variants/CFs?
if (cbch != 0) { // Yes.
PROPASSERT(pcount != NULL); PROPASSERT(*pcount == 0 || ppv != NULL); PROPASSERT(0 == cbByteSwap);
// Start calculating the serialized size. Include the sizes
// of the VT.
cb = sizeof(ULONG); // Is this an Array or Vector of Variants?
if( cbch != MAXULONG ) { // No. Include each element's length field.
cb += *pcount * sizeof(ULONG); }
// For vectors, write the element count
if( VT_VECTOR & pvar->vt ) { cb += sizeof(ULONG);
// Do we have room to write it?
if( *pcb < cb ) { // No. But we'll continue to calculate the cb
pprop = NULL; } else if( pprop != NULL ) { *(ULONG *) pbdst = PropByteSwap(*pcount); pbdst += sizeof(ULONG); } } // if( VT_VECTOR & pvar->vt )
// For arrays, write the dimension count, features, and element size
else if( NULL != parray ) { PROPASSERT( VT_ARRAY & pvar->vt ); ULONG cbBounds = 0, cElems = 0;
// Allow for the VarType & dimension count
cb += sizeof(DWORD); cb += sizeof(UINT); PROPASSERT( sizeof(DWORD) >= sizeof(VARTYPE) );
// Allow for the rgsaBounds
*pstatus = SerializeSafeArrayBounds( parray, NULL, &cbBounds, &cElems ); if( !NT_SUCCESS(*pstatus) ) goto Exit; cb += cbBounds;
// Do we have room to write this?
if( *pcb < cb ) { // No, but continue to calc cb
pprop = NULL; } else if( NULL != pprop ) { // Yes, we have room. Write the safearray header data.
PROPASSERT( sizeof(UINT) == sizeof(ULONG) );
// Write the SafeArray's internal vartype. We'll write the real pvar->vt
// at the bottom of this routine.
*(DWORD *) pbdst = 0; *(VARTYPE *)pbdst = PropByteSwap( static_cast<VARTYPE>(pvar->vt & VT_TYPEMASK) ); pbdst += sizeof(DWORD);
// Write the dimension count
*(UINT *)pbdst = PropByteSwap(cSafeArrayDims); pbdst += sizeof(UINT);
// Write the bounds
*pstatus = SerializeSafeArrayBounds( parray, pbdst, &cbBounds, &cElems ); pbdst += cbBounds; } } // if( VT_VECTOR & pvar->vt ) ... else if
// Walk through the vector/array and write the elements.
for( ulIndex = *pcount; ulIndex > 0; ulIndex-- ) { ULONG cbcopy = 0; const PROPVARIANT *ppropvarT;
// Switch on the size of the element.
switch (cbch) { //
// VT_VARIANT, VT_VECTOR
//
case MAXULONG: cbcopy = MAXULONG;
// Perform a recursive serialization
StgConvertVariantToPropertyNoEH( (PROPVARIANT *) ppv, CodePage, NULL, &cbcopy, PID_ILLEGAL, (VT_VECTOR & pvar->vt) ? TRUE : FALSE, (VT_ARRAY & pvar->vt) ? TRUE : FALSE, NULL, &wMinFormatRequired, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
break;
//
// VT_CF
//
case sizeof(CLIPDATA):
// We copy cbSize-sizeof(ulClipFmt) bytes.
if( ((CLIPDATA *) ppv)->cbSize < sizeof(ULONG) ) { StatusInvalidParameter(pstatus, "StgConvertVariantToProperty: short cbSize on VT_CF"); goto Exit; } else { cbcopy = CBPCLIPDATA( *(CLIPDATA*) ppv ); }
// But increment cb to to include sizeof(ulClipFmt)
cb += sizeof(ULONG); break;
//
// VT_LPWSTR
//
case sizeof(WCHAR): if (*ppv != NULL) { PROPASSERT(IsUnicodeString((WCHAR const *) *ppv, MAXULONG)); cbcopy = ((ULONG)Prop_wcslen((WCHAR *) *ppv) + 1) * sizeof(WCHAR); pv = *ppv;
// If byte-swapping is necessary, swap in units of WCHARs
CBBYTESWAP( sizeof(WCHAR) );
} break;
//
// VT_LPSTR/VT_BSTR
//
default:
PROPASSERT(cbch == sizeof(BYTE)); PROPASSERT(pchConvert == NULL);
if (*ppv != NULL) { pv = *ppv;
// Is this a BSTR?
if( VT_BSTR == (VT_TYPEMASK & pvar->vt) ) { // Initialize the # bytes to copy.
cbcopy = BSTRLEN(pv);
// Verify that the BSTR is terminated.
if( S_OK != StringCbLengthW((const OLECHAR*)pv, cbcopy+sizeof(OLECHAR), NULL )) { StatusInvalidParameter(pstatus, "StgConvertVariantToProperty: bad BSTR array null char"); goto Exit; }
// Also copy the string terminator.
cbcopy += sizeof(OLECHAR);
// If the propset and the BSTR are in mismatched
// codepages (one's Unicode, the other's Ansi),
// correct the BSTR now. In any case, the correct
// string is in 'pv'.
if (CodePage != CP_WINUNICODE // Ansi property set
&& OLECHAR_IS_UNICODE) // Unicode BSTR
{ PROPASSERT(IsUnicodeString((WCHAR*)pv, cbcopy));
PrpConvertToMultiByte( (WCHAR const *) pv, cbcopy, CodePage, &pchConvert, &cbcopy, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
pv = pchConvert; }
else if (CodePage == CP_WINUNICODE // Unicode property set
&& !OLECHAR_IS_UNICODE) // Ansi BSTRs
{ PROPASSERT(IsAnsiString((CHAR const *) pv, cbcopy));
PrpConvertToUnicode( (CHAR const *) pv, cbcopy, CP_ACP, // In-mem BSTR is in system CP
(WCHAR **) &pchConvert, &cbcopy, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// The Unicode string must have the proper byte order
CBBYTESWAP( sizeof(WCHAR) );
pv = pchConvert;
}
else if (CodePage == CP_WINUNICODE ) { // Both the BSTR and the property set are Unicode.
// No conversion is required, but byte-swapping
// is (if byte-swapping is enabled).
CBBYTESWAP( sizeof(WCHAR) ); }
} // if( VT_BSTR == (VT_TYPEMASK & pvar->vt) )
// Otherwise it's an LPSTR
else { PROPASSERT(IsAnsiString((char const *) pv, MAXULONG)); PROPASSERT(pvar->vt == (VT_VECTOR | VT_LPSTR)); cbcopy = (ULONG)strlen((char *) pv) + 1; // + trailing null
if (CodePage == CP_WINUNICODE) { PROPASSERT(IsAnsiString( (CHAR const *) pv, cbcopy)); PrpConvertToUnicode( (CHAR const *) pv, cbcopy, CP_ACP, (WCHAR **) &pchConvert, &cbcopy, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// If byte-swapping, we'll do so with the WCHARs
CBBYTESWAP( sizeof(WCHAR) );
pv = pchConvert; } } // if (pvar->vt == (VT_VECTOR | VT_BSTR)) ... else
} // if (*ppv != NULL)
// In the end, pv should be in the codepage of
// the property set.
#ifdef LITTLEENDIAN
PROPASSERT( NULL == pv || CodePage == CP_WINUNICODE && IsUnicodeString((WCHAR*)pv, cbcopy) || CodePage != CP_WINUNICODE && IsAnsiString((CHAR*)pv, cbcopy)); #endif
break;
} // switch (cbch)
// Add the size of this vector element to the property total
cb += DwordAlign(cbcopy);
// Will there be enough room for this vector element?
if (*pcb < cb) { // No - we'll continue (thus calculating the total size
// necessary), but we won't write to the caller's buffer.
pprop = NULL; }
// Is this a vector or array of Variants?
if (cbch == MAXULONG) { // Yes. Convert this variant.
if (pprop != NULL) { StgConvertVariantToPropertyNoEH( (PROPVARIANT *) ppv, CodePage, (SERIALIZEDPROPERTYVALUE *) pbdst, &cbcopy, PID_ILLEGAL, (VT_VECTOR & pvar->vt) ? TRUE : FALSE, (VT_ARRAY & pvar->vt) ? TRUE : FALSE, NULL, &wMinFormatRequired, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; pbdst += cbcopy; } ppv = (VOID **) Add2Ptr(ppv, sizeof(PROPVARIANT)); } // if (cbch == MAXULONG)
else { // This is a vector/array of strings or clipformats
PROPASSERT( cbch == sizeof(BYTE) || cbch == sizeof(WCHAR) || cbch == sizeof(CLIPDATA));
PROPASSERT(cbchdiv == sizeof(BYTE) || cbchdiv == sizeof(WCHAR));
// Are we writing the serialized property?
if (pprop != NULL) { ULONG cbVectOrArrayElement;
// Calculate the length of the vector/array element.
cbVectOrArrayElement = (ULONG) cbcopy/cbchdiv;
// Is this a ClipData?
if( cbch == sizeof(CLIPDATA) ) { // Adjust the length to include sizeof(ulClipFmt)
cbVectOrArrayElement += sizeof(ULONG);
// Write the vector element length.
*(ULONG *) pbdst = PropByteSwap( cbVectOrArrayElement );
// Advance pbdst & write the clipboard format.
pbdst += sizeof(ULONG); *(ULONG *) pbdst = PropByteSwap( ((CLIPDATA *) ppv)->ulClipFmt ); } else { // Write the vector element length.
*(ULONG *) pbdst = PropByteSwap( cbVectOrArrayElement ); }
// Advance pbdst & write the property data.
pbdst += sizeof(ULONG); RtlCopyMemory( pbdst, cbch == sizeof(CLIPDATA)? ((CLIPDATA *) ppv)->pClipData : pv, cbcopy);
// Zero out the pad bytes.
RtlZeroMemory(pbdst + cbcopy, DwordRemain(cbcopy));
// If byte-swapping is necessary, do so now.
PBSBuffer( pbdst, DwordAlign(cbcopy), cbByteSwap );
// Advance pbdst to the next property.
pbdst += DwordAlign(cbcopy);
} // if (pprop != NULL)
// Advance ppv to point into the PropVariant at the
// next element in the array.
if (cbch == sizeof(CLIPDATA)) { ppv = (VOID **) Add2Ptr(ppv, sizeof(CLIPDATA)); } else { ppv++; CoTaskMemFree( pchConvert ); pchConvert = NULL; } } // if (cbch == MAXULONG) ... else
} // for (cElems = *pcount; cElems > 0; cElems--)
} // if (cbch != 0) // VECTOR/ARRAY of STRING/VARIANT/CF properties
else { // This isn't an array or a vector, or the elements of the array/vector
// aren't Strings, Variants, or CFs.
ULONG cbCopy = cb;
// Adjust cb (the total serialized buffer size) for
// pre-data.
if( pvar->vt != VT_EMPTY ) { // Allow for the VT
cb += sizeof(ULONG); } if( pcount != NULL ) { // Allow for the count field
cb += sizeof(ULONG); } if( pclipfmt != NULL ) { // Allow for the ulClipFmt field.
cb += sizeof(ULONG); } if( pvar->vt == VT_VERSIONED_STREAM ) { // Allow for the version guid
cb += sizeof(pvar->pVersionedStream->guidVersion); } if( VT_ARRAY & pvar->vt ) { // Allow for SafeArray header info
cb += sizeof(DWORD); // VT
cb += sizeof(UINT); // Dimension count
PROPASSERT( sizeof(DWORD) >= sizeof(VARTYPE) );
// Allow for the SafeArray bounds vector
ULONG cbBounds = 0, cElems = 0; *pstatus = SerializeSafeArrayBounds( parray, NULL, &cbBounds, &cElems ); if( !NT_SUCCESS(*pstatus) ) goto Exit; cb += cbBounds; }
/*
if( VT_RECORD == (VT_TYPEMASK & pvar->vt) ) { // Allow for the recinfo guids.
cb += sizeof(GUID); // Type library ID
cb += sizeof(WORD); // Major version
cb += sizeof(WORD); // Minor version
cb += sizeof(LCID); // Type library Locale ID
cb += sizeof(GUID); // Type info ID
PROPASSERT( sizeof(WORD) == sizeof(USHORT) ); // Size of major/minor versions
PROPASSERT( NULL == pcount ); } */
// Is there room in the caller's buffer?
if( *pcb < cb ) { // No - calculate cb but don't write anything.
pprop = NULL; }
// 'pv' should point to the source data. If it does, then
// we'll copy it into the property set. If it doesn't but
// it should, then we'll report an error.
if (pv != NULL || fCheckNullSource) { ULONG cbZero = DwordRemain(cbCopy);
// Do we have a destination (propset) buffer?
if (pprop != NULL) { // Copy the GUID for a VT_VERSIONED_STREAM
if( pvar->vt == VT_VERSIONED_STREAM ) { if( NULL != pvar->pVersionedStream ) *reinterpret_cast<GUID*>(pbdst) = pvar->pVersionedStream->guidVersion; else *reinterpret_cast<GUID*>(pbdst) = GUID_NULL;
PropByteSwap( reinterpret_cast<GUID*>(pbdst) ); pbdst += sizeof(pvar->pVersionedStream->guidVersion); }
// Does this property have a count field?
if( pcount != NULL ) { // Write the count & advance pbdst
*(ULONG *) pbdst = PropByteSwap( *pcount ); pbdst += sizeof(ULONG); }
// Copy the clipfmt for a VT_CF
if( pclipfmt != NULL ) { PROPASSERT(pvar->vt == VT_CF);
// Write the ClipFormat & advance pbdst
*(ULONG *) pbdst = PropByteSwap( (DWORD) *pclipfmt ); pbdst += sizeof(ULONG); }
// Write the array info
if( (VT_ARRAY & pvar->vt) && NULL != parray ) { ULONG cbBounds = 0, cElems = 0;
PROPASSERT( NULL == pcount && NULL == pclipfmt ); PROPASSERT( NULL != parray ); PROPASSERT( 0 != cSafeArrayDims ); PROPASSERT( VT_ARRAY & pvar->vt ); PROPASSERT( sizeof(UINT) == sizeof(ULONG) );
*(DWORD *) pbdst = 0; *(VARTYPE *)pbdst = PropByteSwap( static_cast<VARTYPE>(pvar->vt & VT_TYPEMASK) ); pbdst += sizeof(DWORD);
*(UINT *)pbdst = PropByteSwap(cSafeArrayDims); pbdst += sizeof(UINT);
*pstatus = SerializeSafeArrayBounds( parray, pbdst, &cbBounds, &cElems ); pbdst += cbBounds;
} } // if (pprop != NULL)
// Are we missing the source data?
if (pv == NULL) { // The Source pointer is NULL. If cbCopy != 0, the passed
// VARIANT is not properly formed.
if (cbCopy != 0) { StatusInvalidParameter(pstatus, "StgConvertVariantToProperty: bad NULL"); goto Exit; } } else if (pprop != NULL) { // We have a non-NULL source & destination.
// First, copy the bytes from the former to the latter.
RtlCopyMemory(pbdst, pv, cbCopy);
// Then, if necessary, swap the bytes in the property
// set (leaving the PropVariant bytes untouched).
PBSBuffer( (VOID*) pbdst, cbCopy, cbByteSwap );
// If this is a decimal, zero-out the reserved word at the front
// (typically, this is actually the VarType, because of the
// way in which a decimal is stored in a Variant).
if( VT_DECIMAL == (~VT_BYREF & pvar->vt) ) *(WORD *) pbdst = 0;
}
// Did we write the serialization?
if (pprop != NULL) { // Zero the padding bytes.
RtlZeroMemory(pbdst + cbCopy, cbZero);
// Canonicalize VARIANT_BOOLs. We do this here because
// we don't want to muck with the caller's buffer directly.
if ((pvar->vt & ~VT_VECTOR) == VT_BOOL) { VARIANT_BOOL *pvb = (VARIANT_BOOL *) pbdst; VARIANT_BOOL *pvbEnd = &pvb[cbCopy/sizeof(*pvb)];
while (pvb < pvbEnd) { if (*pvb && PropByteSwap(*pvb) != VARIANT_TRUE) { DebugTrace(0, DEBTRACE_ERROR, ( "Patching VARIANT_TRUE value: %hx --> %hx\n", *pvb, VARIANT_TRUE));
*pvb = PropByteSwap( (VARIANT_BOOL) VARIANT_TRUE ); } pvb++; } } } // if (pprop != NULL)
} } // if (cbch != 0) ... else // non - STRING/VARIANT/CF VECTOR property
// Set the VT in the serialized buffer now that all size
// checks completed.
if (pprop != NULL && pvar->vt != VT_EMPTY) { // When byte-swapping the VT, treat it as a DWORD
// (it's a WORD in the PropVariant, but a DWORD when
// serialized).
pprop->dwType = PropByteSwap( static_cast<DWORD>(~VT_BYREF & pvar->vt) ); }
// Update the caller's copy of the total size.
*pcb = DwordAlign(cb);
Exit:
if( fSafeArrayLocked ) { PROPASSERT( NULL != parraydata ); PROPASSERT( NULL != parray );
PrivSafeArrayUnaccessData( const_cast<SAFEARRAY*>(parray) ); parraydata = NULL; }
/*
if( NULL != pTypeInfo ) pTypeInfo->Release(); if( NULL != pTypeLib ) pTypeLib->Release(); */
if( NULL != pwMinFormatRequired ) *pwMinFormatRequired = wMinFormatRequired;
CoTaskMemFree( pchConvert ); return(pprop);
}
//+---------------------------------------------------------------------------
// Function: StgConvertPropertyToVariant, private
//
// Synopsis: Convert a SERIALIZEDPROPERTYVALUE to a PROPVARIANT
//
// Arguments: [pprop] -- pointer to SERIALIZEDPROPERTYVALUE
// [PointerDelta] -- adjustment to pointers to get user addresses
// [fConvertNullStrings] -- map NULL strings to empty strings
// [CodePage] -- property set codepage
// [pvar] -- pointer to PROPVARIANT
// [pma] -- caller's memory allocation routine
// [pstatus] -- pointer to NTSTATUS code
//
// Returns: TRUE if property is an indirect property type
//
// NOTE: This routine assumes that pprop points to a semi-valid
// serialized property value. That is, pprop points to
// a property value, and its buffer is long enough to hold
// the buffer. This is ensured by calling PropertyLength first.
// PropertyLength doesn't, however, ensure that individual fields
// are correct, only that the length prefix is valid. E.g.
// it doesn't check the individual fields of a VT_CF, it doesn't
// verify that strings are terminated, etc.
//
//---------------------------------------------------------------------------
#ifdef KERNEL
#define ADJUSTPOINTER(ptr, delta, type) (ptr) = (type) Add2Ptr((ptr), (delta))
#else
#define ADJUSTPOINTER(ptr, delta, type)
#endif
// First, define a wrapper for this function which returns errors
// using NT Exception Handling, rather than returning an NTSTATUS.
// This is done for backwards compatibility with old CI implementations.
#if defined(WINNT)
EXTERN_C BOOLEAN __stdcall StgConvertPropertyToVariant( IN SERIALIZEDPROPERTYVALUE const *pprop, IN USHORT CodePage, OUT PROPVARIANT *pvar, IN PMemoryAllocator *pma) { BOOLEAN boolRet; NTSTATUS status;
boolRet = StgConvertPropertyToVariantNoEH( pprop, -1, CodePage, pvar, pma, &status );
if (!NT_SUCCESS( status )) RtlRaiseStatus( status );
return (boolRet);
}
#endif // #if defined(WINNT)
// Now define the body of the function, returning errors with an
// NTSTATUS value instead of raising.
BOOLEAN StgConvertPropertyToVariantNoEH( IN SERIALIZEDPROPERTYVALUE const *pprop, IN ULONG cbprop, IN USHORT CodePage, OUT PROPVARIANT *pvar, IN PMemoryAllocator *pma, OUT NTSTATUS *pstatus) { *pstatus = STATUS_SUCCESS;
// ------
// Locals
// ------
BOOLEAN fIndirect = FALSE;
// Buffers which must be freed before exiting.
CHAR *pchConvert = NULL, *pchByteSwap = NULL;
VOID **ppv = NULL; VOID *pv = NULL; const VOID *pvCountedString = NULL;
VOID *pvSafeArrayData = NULL; SAFEARRAY *psa = NULL; BOOL fSafeArrayLocked = FALSE;
ULONG cbskip = sizeof(ULONG);
ULONG cb = 0;
// Size of byte-swapping units (must be signed).
INT cbByteSwap = 0;
BOOLEAN fPostAllocInit = FALSE; BOOLEAN fNullLegal = (BOOLEAN) ( (PropByteSwap(pprop->dwType) & VT_VECTOR) != 0 );
IFDBG( HRESULT &hr = *pstatus; ) propITraceStatic( "StgConvertPropertyToVariantNoEH" ); propTraceParameters(( "pprop=%p, CodePage=%d, pvar=%p, pma=%p" ));
// ---------------------------------------------------------
// Based on the VT, calculate ppv, pv, cbskip,
// cb, fPostAllocInit, cbCheck, fNullLegal
// ---------------------------------------------------------
// Set the VT in the PropVariant. Note that in 'pprop' it's a
// DWORD, but it's a WORD in 'pvar'.
pvar->vt = (VARTYPE) PropByteSwap(pprop->dwType);
if( VT_BYREF & pvar->vt ) { // ByRef's are always indirected on their way to the property set.
// Thus we should never see a VT_BYREF in serialized form.
StatusError( pstatus, "StgConvertPropertyToVariant found VT_BYREF", STATUS_INTERNAL_DB_CORRUPTION ); goto Exit; }
//
// Switch on the type and set cb, pv, etc. The information stored in these
// is subsequently used to do most of the work. In many cases, we can just do
// a copy at the end of the switch, we copy cb bytes from pprop->rgb into the buffer
// pointed to by pv (which points into the output propvariant). If an allocation
// must be done, ppv points to the location in the propvariant that should hold
// the buffer, so we allocate at buffer at *ppv, then copy cb bytes into it.
//
switch( pvar->vt ) { case VT_EMPTY: case VT_NULL: break;
case VT_I1: //AssertByteField(cVal); // VT_I1
cb = sizeof(pvar->cVal); pv = &pvar->cVal; break;
case VT_UI1: AssertByteField(bVal); // VT_UI1
cb = sizeof(pvar->bVal); pv = &pvar->bVal; break;
case VT_I2: case VT_UI2: case VT_BOOL: AssertShortField(iVal); // VT_I2
AssertShortField(uiVal); // VT_UI2
AssertShortField(boolVal); // VT_BOOL
cb = sizeof(pvar->iVal); pv = &pvar->iVal;
// If swapping, swap as a WORD
CBBYTESWAP(cb); break;
case VT_I4: case VT_INT: case VT_UI4: case VT_UINT: case VT_R4: case VT_ERROR: AssertLongField(lVal); // VT_I4
//AssertLongField(intVal) // VT_INT
AssertLongField(ulVal); // VT_UI4
//AssertLongField(uintVal); // VT_UINT
AssertLongField(fltVal); // VT_R4
AssertLongField(scode); // VT_ERROR
cb = sizeof(pvar->lVal); pv = &pvar->lVal;
// If swapping, swap as a DWORD
CBBYTESWAP(cb); break;
case VT_I8: case VT_UI8: case VT_FILETIME: AssertLongLongField(hVal); // VT_I8
AssertLongLongField(uhVal); // VT_UI8
AssertLongLongField(filetime); // VT_FILETIME
cb = sizeof(pvar->hVal); pv = &pvar->hVal;
// If swapping, swap as a pair of DWORDs
CBBYTESWAP(sizeof(DWORD)); break;
case VT_R8: case VT_CY: case VT_DATE: AssertLongLongField(dblVal); // VT_R8
AssertLongLongField(cyVal); // VT_CY
AssertLongLongField(date); // VT_DATE
cb = sizeof(pvar->dblVal); pv = &pvar->dblVal;
// If swapping, swap as a LONGLONG
CBBYTESWAP(cb); break;
case VT_CLSID: AssertStringField(puuid); // VT_CLSID
cb = sizeof(GUID); ppv = (VOID **) &pvar->puuid; cbskip = 0;
// If swapping, special handling is required
CBBYTESWAP( CBBYTESWAP_UID ); break;
case VT_DECIMAL: //AssertVarField(decVal, sizeof(DECIMAL)); // VT_DECIMAL
cb = sizeof(DECIMAL); pv = (VOID *) &pvar->decVal;
#ifdef BIGENDIAN
#error Big-Endian support required
// Define CBBYTESWAP_DECIMAL, and add support for it below
//CBBYTESWAP( CBBYTESWAP_DECIMAL );
#endif
break;
case VT_CF:
// Allocate a CLIPDATA buffer. Init-zero it so that we can
// do a safe cleanup should an early-exist be necessary.
pvar->pclipdata = (CLIPDATA *) pma->Allocate(sizeof(CLIPDATA)); if (pvar->pclipdata == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory for CF"); goto Exit; } RtlZeroMemory( pvar->pclipdata, sizeof(CLIPDATA) );
// Set the size (includes sizeof(ulClipFmt))
pvar->pclipdata->cbSize = PropByteSwap( ((CLIPDATA *) pprop->rgb)->cbSize ); if( pvar->pclipdata->cbSize < sizeof(pvar->pclipdata->ulClipFmt) ) { StatusError(pstatus, "StgConvertPropertyToVariant: Invalid VT_CF cbSize", STATUS_INTERNAL_DB_CORRUPTION); goto Exit; }
// Set the # bytes-to-copy. We can't use the CBPCLIPDATA macro
// here because it assumes that the CLIPDATA parameter is correctly
// byte-swapped.
cb = PropByteSwap( *(DWORD*) pprop->rgb ) - sizeof(pvar->pclipdata->ulClipFmt);
// Set the ClipFormat itself.
pvar->pclipdata->ulClipFmt = PropByteSwap( ((CLIPDATA *) pprop->rgb)->ulClipFmt );
// Prepare for the alloc & copy. Put the buffer pointer
// in pClipData, & skip the ulClipFmt in the copy.
ppv = (VOID **) &pvar->pclipdata->pClipData; cbskip += sizeof(ULONG);
// It's legal for cb to be 0.
fNullLegal = TRUE;
// Adjust to the user-mode pointer (Kernel only)
ADJUSTPOINTER(pvar->pclipdata, PointerDelta, CLIPDATA *);
break;
case VT_BLOB: case VT_BLOB_OBJECT:
cb = pvar->blob.cbSize = PropByteSwap( *(ULONG *) pprop->rgb ); ppv = (VOID **) &pvar->blob.pBlobData; fNullLegal = TRUE; break;
case VT_VERSIONED_STREAM:
// Allocate the first buffer (which will point to the stream)
pvar->pVersionedStream = reinterpret_cast<LPVERSIONEDSTREAM>( pma->Allocate( sizeof(*pvar->pVersionedStream) )); if (pvar->pVersionedStream == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory for VersionedStream"); goto Exit; } RtlZeroMemory( pvar->pVersionedStream, sizeof(*pvar->pVersionedStream) );
// Read the GUID (the PropertyLength function has already
// validated that the buffer is big enough to read the GUID
// and counted string.
pvar->pVersionedStream->guidVersion = *reinterpret_cast<const GUID*>( pprop->rgb ); PropByteSwap( &pvar->pVersionedStream->guidVersion );
// A buffer will be allocated and the stream name put into *ppv.
ppv = reinterpret_cast<void**>( &pvar->pVersionedStream->pStream );
// Point to the beginning of the string
pvCountedString = Add2Ptr( pprop->rgb, sizeof(GUID) );
// When copying the string, we will skip the guid
cbskip += sizeof(GUID);
// Fall through
case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT: fIndirect = TRUE; goto lpstr;
case VT_BSTR: case VT_LPSTR: lpstr: AssertStringField(pszVal); // VT_STREAM, VT_STREAMED_OBJECT
AssertStringField(pszVal); // VT_STORAGE, VT_STORED_OBJECT
AssertStringField(bstrVal); // VT_BSTR
AssertStringField(pszVal); // VT_LPSTR
// The string to be converted is loaded into pvCountedString
if( NULL == pvCountedString ) pvCountedString = reinterpret_cast<const void*>(pprop->rgb);
// [length field] bytes should be allocated
cb = PropByteSwap( *(ULONG *) pvCountedString );
// When a buffer is allocated, its pointer will go
// in *ppv.
if( NULL == ppv ) ppv = (VOID **) &pvar->pszVal;
// Is this a non-empty string?
if (cb != 0) { // Yes, non-empty. If the serialized string is Unicode, ensure
// that the cb is even
if( CodePage == CP_WINUNICODE && 0 != (cb % 2) ) { StatusError(pstatus, "StgConvertPropertyToVariant: Odd Unicode string cb", STATUS_INTERNAL_DB_CORRUPTION); goto Exit; }
// Also ensure that the string is terminated
if( CodePage == CP_WINUNICODE ) { WCHAR *pwsz = (WCHAR *) Add2ConstPtr(pvCountedString, sizeof(ULONG)); //if( L'\0' != pwsz[ cb/sizeof(WCHAR) - 1 ] )
if( S_OK != StringCbLengthW(pwsz, cb, NULL )) { StatusError(pstatus, "StgConvertPropertyToVariant: Unterminated string", STATUS_INTERNAL_DB_CORRUPTION ); goto Exit; } } else { CHAR *psz = (CHAR *) Add2ConstPtr(pvCountedString, sizeof(ULONG)); //if( '\0' != psz[ cb - 1 ] )
if( S_OK != StringCbLengthA(psz, cb, NULL )) { StatusError(pstatus, "StgConvertPropertyToVariant: Unterminated string", STATUS_INTERNAL_DB_CORRUPTION ); goto Exit; } }
// Is the serialized value one that should be
// an Ansi string in the PropVariant (if so it might require conversion)?
if (pvar->vt == VT_LPSTR // It's an LPSTR (always Ansi), or
|| !OLECHAR_IS_UNICODE ) // PropVariant strings are Ansi (Mac)
{ // If the propset is Unicode, we must do a
// conversion to Ansi.
if (CodePage == CP_WINUNICODE) { WCHAR *pwsz = (WCHAR *) Add2ConstPtr(pvCountedString, sizeof(ULONG));
// If necessary, swap the WCHARs. 'pwsz' will point to
// the correct (system-endian) string either way. If an
// alloc is necessary, 'pchByteSwap' will point to the new
// buffer.
PBSInPlaceAlloc( &pwsz, (WCHAR**) &pchByteSwap, pstatus ); if( !NT_SUCCESS( *pstatus )) goto Exit; PROPASSERT(IsUnicodeString( pwsz, cb));
// Convert the properly-byte-ordered string in 'pwsz'
// into MBCS, putting the result in pchConvert.
// This routine will validate that cb is even.
PrpConvertToMultiByte( pwsz, cb, CP_ACP, // Use the system default codepage
&pchConvert, &cb, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; } } // if (pvar->vt == VT_LPSTR) ...
// Otherwise, even though this string may be
// Ansi in the Property Set, it must be Unicode
// in the PropVariant.
else { // If necessary, convert to Unicode
if (CodePage != CP_WINUNICODE) { PROPASSERT( IsAnsiString( (CHAR const *) Add2ConstPtr(pvCountedString, sizeof(ULONG)), cb));
PrpConvertToUnicode( (CHAR const *) Add2ConstPtr(pvCountedString, sizeof(ULONG)), cb, CodePage, (WCHAR **) &pchConvert, &cb, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
} // if (CodePage != CP_WINUNICODE)
else { // The value is Unicode both the property set
// and the PropVariant. If byte-swapping is
// necessary, we'll do so in units of WCHARs.
CBBYTESWAP( sizeof(WCHAR) ); }
} // if (pvar->vt == VT_LPSTR) ... else
} // if (cb != 0)
fNullLegal = TRUE; break;
case VT_LPWSTR:
fNullLegal = TRUE; AssertStringField(pwszVal); // VT_LPWSTR
// Show where buffer needs to be allocated.
ppv = (VOID **) &pvar->pwszVal;
// Calculate the length of the Unicode string. Put the total number
// of bytes for this property in cbCheck.
cb = PropByteSwap( *(ULONG *) pprop->rgb ) * sizeof(WCHAR);
// Ensure the string is null-terminated.
if( 0 != cb ) { WCHAR *pwsz = (WCHAR *) Add2ConstPtr(pprop->rgb, sizeof(ULONG)); //if( L'\0' != pwsz[ cb/sizeof(WCHAR) - 1 ] )
if( S_OK != StringCbLengthW(pwsz, cb, NULL )) { StatusError(pstatus, "StgConvertPropertyToVariant: Unterminated string", STATUS_INTERNAL_DB_CORRUPTION ); goto Exit; } }
// If byte-swapping will be necessary, do so for the WCHARs
CBBYTESWAP( sizeof(WCHAR) );
break;
//
// VT_VECTOR types are handled by a recursive call.
//
case VT_VECTOR | VT_I1: //AssertByteVector(cac); // VT_I1
case VT_VECTOR | VT_UI1: AssertByteVector(caub); // VT_UI1
pvar->caub.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->caub.cElems * sizeof(pvar->caub.pElems[0]); ppv = (VOID **) &pvar->caub.pElems;
break;
case VT_VECTOR | VT_I2: case VT_VECTOR | VT_UI2: case VT_VECTOR | VT_BOOL: AssertShortVector(cai); // VT_I2
AssertShortVector(caui); // VT_UI2
AssertShortVector(cabool); // VT_BOOL
pvar->cai.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->cai.cElems * sizeof(pvar->cai.pElems[0]); ppv = (VOID **) &pvar->cai.pElems;
// If swapping, swap as a WORD
CBBYTESWAP(sizeof(pvar->cai.pElems[0])); break;
case VT_VECTOR | VT_I4: case VT_VECTOR | VT_UI4: case VT_VECTOR | VT_R4: case VT_VECTOR | VT_ERROR: AssertLongVector(cal); // VT_I4
AssertLongVector(caul); // VT_UI4
AssertLongVector(caflt); // VT_R4
AssertLongVector(cascode); // VT_ERROR
pvar->cal.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->cal.cElems * sizeof(pvar->cal.pElems[0]); ppv = (VOID **) &pvar->cal.pElems;
// If byte swapping, swap as DWORDs
CBBYTESWAP(sizeof(pvar->cal.pElems[0])); break;
case VT_VECTOR | VT_I8: case VT_VECTOR | VT_UI8: case VT_VECTOR | VT_FILETIME: AssertLongLongVector(cah); // VT_I8
AssertLongLongVector(cauh); // VT_UI8
AssertLongLongVector(cafiletime); // VT_FILETIME
pvar->cah.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->cah.cElems * sizeof(pvar->cah.pElems[0]); ppv = (VOID **) &pvar->cah.pElems;
// If byte swapping, swap as DWORDs
CBBYTESWAP(sizeof(DWORD)); break;
case VT_VECTOR | VT_R8: case VT_VECTOR | VT_CY: case VT_VECTOR | VT_DATE: AssertLongLongVector(cadbl); // VT_R8
AssertLongLongVector(cacy); // VT_CY
AssertLongLongVector(cadate); // VT_DATE
pvar->cadbl.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->cadbl.cElems * sizeof(pvar->cadbl.pElems[0]); ppv = (VOID **) &pvar->cadbl.pElems;
// If byte swapping, swap as LONGLONGs
CBBYTESWAP(sizeof(pvar->cadbl.pElems[0])); break;
case VT_VECTOR | VT_CLSID: AssertVarVector(cauuid, sizeof(GUID)); pvar->cauuid.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->cauuid.cElems * sizeof(pvar->cauuid.pElems[0]); ppv = (VOID **) &pvar->cauuid.pElems;
// If byte swapping, special handling is required.
CBBYTESWAP( CBBYTESWAP_UID ); break;
case VT_VECTOR | VT_CF:
// Set the count of clipdatas
pvar->caclipdata.cElems = PropByteSwap( *(ULONG *) pprop->rgb );
// How much should we allocate for caclipdata.pElems, & where
// should that buffer pointer go?
cb = pvar->caclipdata.cElems * sizeof(pvar->caclipdata.pElems[0]); ppv = (VOID **) &pvar->caclipdata.pElems;
// We need to do work after pElems is allocated.
fPostAllocInit = TRUE; break;
case VT_VECTOR | VT_BSTR: case VT_VECTOR | VT_LPSTR: AssertStringVector(cabstr); // VT_BSTR
AssertStringVector(calpstr); // VT_LPSTR
// Put the element count in the PropVar
pvar->calpstr.cElems = PropByteSwap( *(ULONG *) pprop->rgb );
// An array of cElems pointers should be alloced
cb = pvar->calpstr.cElems * sizeof(CHAR*);
// Show where the array of pointers should go.
ppv = (VOID **) &pvar->calpstr.pElems;
// Additional allocs will be necessary after the vector
// is alloced.
fPostAllocInit = TRUE;
break;
case VT_VECTOR | VT_LPWSTR:
AssertStringVector(calpwstr); // VT_LPWSTR
pvar->calpwstr.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->calpwstr.cElems * sizeof(WCHAR *); ppv = (VOID **) &pvar->calpwstr.pElems; fPostAllocInit = TRUE; break;
case VT_VECTOR | VT_VARIANT:
AssertVariantVector(capropvar); // VT_VARIANT
pvar->capropvar.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); cb = pvar->capropvar.cElems * sizeof(PROPVARIANT); ppv = (VOID **) &pvar->capropvar.pElems; fPostAllocInit = TRUE; break;
//
// VT_ARRAY (SafeArray) types are handled by a recursive call
//
case VT_ARRAY | VT_BSTR: cbskip = 0; cb = sizeof(BSTR); // (BSTR is really a pointer)
ppv = (VOID**) &pvar->parray; fPostAllocInit = TRUE; break; case VT_ARRAY | VT_VARIANT: cbskip = 0; cb = sizeof(PROPVARIANT); ppv = (VOID**) &pvar->parray; fPostAllocInit = TRUE; break;
case VT_ARRAY | VT_I1: case VT_ARRAY | VT_UI1: cbskip = 0; ppv = (VOID**) &pvar->parray; cb = sizeof(BYTE); break;
case VT_ARRAY | VT_I2: case VT_ARRAY | VT_UI2: case VT_ARRAY | VT_BOOL: cbskip = 0; ppv = (VOID**) &pvar->parray; cb = sizeof(USHORT); break;
case VT_ARRAY | VT_I4: case VT_ARRAY | VT_UI4: case VT_ARRAY | VT_INT: case VT_ARRAY | VT_UINT: case VT_ARRAY | VT_R4: case VT_ARRAY | VT_ERROR: cbskip = 0; ppv = (VOID**) &pvar->parray; cb = sizeof(ULONG); break;
case VT_ARRAY | VT_DECIMAL: cbskip = 0; ppv = (VOID**) &pvar->parray; cb = sizeof(DECIMAL); break;
/*
case VT_ARRAY | VT_I8: case VT_ARRAY | VT_UI8: */ case VT_ARRAY | VT_DATE: cbskip = 0; ppv = (VOID**) &pvar->parray; cb = sizeof(ULONGLONG);
// If byte swapping, swap as DWORDs
CBBYTESWAP(DWORD);
break;
case VT_ARRAY | VT_R8: case VT_ARRAY | VT_CY: cbskip = 0; ppv = (VOID**) &pvar->parray; cb = sizeof(CY);
// If byte swapping, swap as LONGLONGs
CBBYTESWAP(cb);
break;
default: propDbg(( DEB_IWARN, "StgConvertPropertyToVariant: unsupported vt=%d\n", pvar->vt )); *pstatus = STATUS_NOT_SUPPORTED; goto Exit;
} // switch (pvar->vt)
// ------------------------------------------------------
// We've now analyzed the serialized property, learned
// about it, and loaded part or all of the PropVariant.
// Now we can load any remaining parts.
// ------------------------------------------------------
// Is this a simple, unaligned scalar?
if (pv != NULL) { // Yes. All we need to do is copy some bytes.
PROPASSERT(pchConvert == NULL); PROPASSERT( cb < sizeof(PROPVARIANT)-sizeof(VARTYPE) || VT_DECIMAL == pprop->dwType );
// Copy from the pprop into the PropVariant
RtlCopyMemory(pv, pprop->rgb, cb);
// We also might need to byte-swap them (but only in the PropVar).
PBSBuffer( pv, cb, cbByteSwap );
// Decimal requires special handling, since it overlaps the VT field.
if( VT_DECIMAL == PropByteSwap(pprop->dwType) ) pvar->vt = VT_DECIMAL;
} // if (pv != NULL)
// Otherwise, we need to allocate memory, to which the
// PropVariant will point.
else if (ppv != NULL) { *ppv = NULL;
// If cb is zero, then there's nothing to allocate anyway.
if( cb == 0 ) { // Make sure it's legal for this type to be NULL.
if (!fNullLegal) { StatusInvalidParameter(pstatus, "StgConvertPropertyToVariant: bad NULL"); goto Exit; } }
else { // We need to allocate something.
SAFEARRAYBOUND *rgsaBounds = NULL; ULONG cElems = 0, cbBounds = 0;
PROPASSERT(cb != 0);
// Allocate the necessary buffer (which we figured out in the
// switch above). For vector properties, this will just be the
// pElems buffer at this point. For singleton BSTR properties, we'll skip
// this allocate altogether; they're allocated by oleaut with SysStringAlloc.
// For array properties, we'll allocate the safearray and the buffer that
// it will reference.
if( VT_ARRAY & pvar->vt ) { // This is a SafeArray. We need to use oleaut to allocate the SafeArray
// structure.
VARTYPE vtInternal; // The VT as determined by the SafeArray
UINT cDims = 0;
// Read the SafeArray's internal VT
// (PropertyLength guarantees us that we can read the VT,
// cDims, and bounds).
vtInternal = *(VARTYPE*) &pprop->rgb[cbskip]; cbskip += sizeof(ULONG);
// Read the dimension count
cDims = *(ULONG*) &pprop->rgb[cbskip]; cbskip += sizeof(DWORD);
// Point to the SAFEARRAYBOUND array
rgsaBounds = (SAFEARRAYBOUND*) &pprop->rgb[cbskip];
// We now have everything we need to create a new safe array
psa = PrivSafeArrayCreateEx( vtInternal, cDims, rgsaBounds, NULL ); if( NULL == psa ) { propDbg(( DEB_ERROR, "Failed SafeArrayCreateEx, vt=0x%x, cDims=%d\n", vtInternal, cDims )); *pstatus = STATUS_NO_MEMORY; goto Exit; } cbskip += cDims * sizeof(SAFEARRAYBOUND);
// Calculate the number of elements in the safearray.
PROPASSERT( cb == psa->cbElements ); *pstatus = SerializeSafeArrayBounds( psa, NULL, &cbBounds, &cElems ); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// In the big switch above, cb was set to the single element size.
// Now update it to be the total element size.
cb *= cElems;
// Put this SafeArray into pvar->parray
*ppv = psa;
// Get the newly-created psa->pvData
*pstatus = PrivSafeArrayAccessData( psa, &pvSafeArrayData ); if( FAILED(*pstatus) ) goto Exit; fSafeArrayLocked = TRUE;
// Point ppv to it - we'll copy the data from the serialized
// format to here.
ppv = &pvSafeArrayData; PROPASSERT( NULL != ppv && psa != *ppv );
} // if( VT_ARRAY & pvar->vt )
else if( VT_BSTR != pvar->vt ) { // Array was handled in the if above, BSTRs are handled below
// (simultaneous with the copy), so this else if block is for
// everything else - i.e. simple allocs.
*ppv = pma->Allocate(max(1, cb)); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory"); goto Exit; } } // if( VT_ARRAY & pvar->vt ) ... else if( VT_BSTR != pvar->vt )
// We've got memory allocated now.
// Can we load the PropVariant with a simple copy?
if( !fPostAllocInit ) { // Yes - all we need is a memcopy (and an implicit alloc for BSTRs).
if (VT_BSTR == pvar->vt) { // We do the copy with the OleAutomation routine
// (which does an allocation too).
//
// If byte-swapping is necessary, the switch block
// already took care of it, leaving the buffer in
// 'pchConvert'.
//
// We already validated that the string is properly terminated.
// Now do the alloc/copy
PROPASSERT( NULL == *ppv ); *ppv = PrivSysAllocString( ( pchConvert != NULL ) ? (OLECHAR *) pchConvert : (OLECHAR *) (pprop->rgb + cbskip) ); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory"); goto Exit; } } // if (VT_BSTR == pvar->vt)
else { // Copy the property into the PropVariant.
RtlCopyMemory( *ppv, pchConvert != NULL? (BYTE const *) pchConvert : pprop->rgb + cbskip, cb);
}
// If necessary, byte-swap the property (only in the PropVar).
PBSBuffer( *ppv, cb, cbByteSwap );
} // if (!fPostAllocInit)
else { // We must do more than just a simple copy.
// (Thus this is a vector/array of strings, variants, or CFs).
// Pointer to the correct location in pprop->rgb
BYTE const *pbsrc;
// Point pbsrc to the actual data (i.e. beyond the counts). For
// vectors, put the element count in cElems.
if( VT_VECTOR & pvar->vt ) { // Get the element count
cElems = pvar->calpstr.cElems;
// Initialize the source pointer to point just beyond
// the element count.
pbsrc = pprop->rgb + sizeof(ULONG); } else { PROPASSERT( VT_ARRAY & pvar->vt ); PROPASSERT( 0 != cElems );
// Initialize the source pointer to point just beyond the VT, cDims, and bounds
pbsrc = pprop->rgb + cbBounds + sizeof(DWORD) + sizeof(UINT); }
// Zero all pointers in the pElems array for easy caller cleanup
// (cb is the size of the array of pointers)
ppv = (VOID **) *ppv; RtlZeroMemory(ppv, cb);
// Handle Variants, ClipFormats, & Strings separately.
if( (VT_VECTOR | VT_VARIANT) == pvar->vt || (VT_ARRAY | VT_VARIANT) == pvar->vt ) { // This is an array or vector of variants
PROPVARIANT *pvarT = (PROPVARIANT *) ppv; PROPASSERT(!fIndirect);
// Loop through the variants.
while (cElems-- > 0) { ULONG cbelement;
// Get this variant into pvarT (which is really part of
// the pvar->capropvar->pElems).
fIndirect = StgConvertPropertyToVariantNoEH( (SERIALIZEDPROPERTYVALUE const *) pbsrc, cbprop - (ULONG)((UINT_PTR)pbsrc - (UINT_PTR)pprop), CodePage, pvarT, pma, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; PROPASSERT(!fIndirect);
// Calculate this size of this serialized element.
cbelement = PropertyLengthNoEH( (SERIALIZEDPROPERTYVALUE const *) pbsrc, cbprop - (ULONG)((UINT_PTR)pbsrc - (UINT_PTR)pprop), CPSS_VARIANTVECTOR, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// Advance pbsrc by that size, and advance pvarT to the
// next element in pvar->capropvar->pElems.
pbsrc += cbelement; pvarT++; } } // if (pvar->vt == (VT_VECTOR | VT_VARIANT) ... )
else if (pvar->vt == (VT_VECTOR | VT_CF)) { // Handle vectors of clipformats.
// Set pcd to &pElems[0]
CLIPDATA *pcd = (CLIPDATA *) ppv;
// Loop through pElems
while (cElems-- > 0) { // What is the size of the clipdata (including sizeof(ulClipFmt))?
pcd->cbSize = PropByteSwap( ((CLIPDATA *) pbsrc)->cbSize ); if( pcd->cbSize < sizeof(pcd->ulClipFmt) ) { StatusError(pstatus, "StgConvertPropertyToVariant: Invalid VT_CF cbSize", STATUS_INTERNAL_DB_CORRUPTION); goto Exit; }
// How many bytes should we copy to pClipData?
cb = CBPCLIPDATA( *pcd );
// Set the ClipFormat & advance pbsrc to the clipdata.
pcd->ulClipFmt = PropByteSwap( ((CLIPDATA *) pbsrc)->ulClipFmt );
pbsrc += 2 * sizeof(ULONG);
// Copy the ClipData into the PropVariant
pcd->pClipData = NULL; if (cb > 0) { // Get a buffer for the clip data.
pcd->pClipData = (BYTE *) pma->Allocate(cb); if (pcd->pClipData == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory for CF[]"); goto Exit; }
// Copy the clipdata into pElems[i].pClipData
RtlCopyMemory(pcd->pClipData, pbsrc, cb); ADJUSTPOINTER(pcd->pClipData, PointerDelta, BYTE *);
} // if (cb > 0)
// Move pcd to &pElems[i+1], and advance the buffer pointer.
pcd++; pbsrc += DwordAlign(cb);
} // while (cElems-- > 0)
} // else if (pvar->vt == (VT_VECTOR | VT_CF))
else { // This is a vector or array of some kind of string.
// Assume that characters are CHARs
ULONG cbch = sizeof(char);
if( pvar->vt == (VT_VECTOR | VT_LPWSTR) ) { // Characters are actually WCHARs
cbch = sizeof(WCHAR);
// If byte-swapping is enabled, LPWSTRs must have
// their WCHARs swapped.
CBBYTESWAP( sizeof(WCHAR) ); }
// Loop through the array
while (cElems-- > 0) { ULONG cbcopy;
// Read the cb from the front of the property, and advance
// pbsrc beyond that length field.
cbcopy = cb = PropByteSwap( *((ULONG *) pbsrc) ) * cbch; pbsrc += sizeof(ULONG); pv = (VOID *) pbsrc;
PROPASSERT(*ppv == NULL); PROPASSERT(pchConvert == NULL);
// Do we have actual data to work with?
if( cb != 0 ) { // Validate that the string is null terminated
if( CP_WINUNICODE == CodePage //&& L'\0' != ((WCHAR*)pv)[ (cb-1) / sizeof(WCHAR) ]
&& S_OK != StringCbLengthW((WCHAR*)pv, cb, NULL ) || CP_WINUNICODE != CodePage //&& '\0' != ((CHAR*)pv)[ cb-1 ]
&& S_OK != StringCbLengthA((CHAR*)pv, cb, NULL ) ) { StatusError(pstatus, "String element missing null termination", STATUS_INTERNAL_DB_CORRUPTION ); goto Exit; }
// Special BSTR pre-processing ... it might be
// necessary to do a unicode/ansi conversion.
if( (VT_VECTOR | VT_BSTR) == pvar->vt || (VT_ARRAY | VT_BSTR) == pvar->vt ) { // If the propset & in-memory BSTRs are of
// different Unicode-ness, convert now.
if (CodePage != CP_WINUNICODE // Ansi PropSet
&& OLECHAR_IS_UNICODE ) // Unicode BSTRs
{
PROPASSERT(IsAnsiString((CHAR*) pv, cb)); PrpConvertToUnicode( (CHAR const *) pv, cb, CodePage, (WCHAR **) &pchConvert, &cbcopy, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; pv = pchConvert; }
else if (CodePage == CP_WINUNICODE // Unicode PropSet
&& !OLECHAR_IS_UNICODE ) // Ansi BSTRs
{ // If byte-swapping is necessary, the string from
// the propset must be swapped before it can be
// converted to MBCS. If such a conversion
// is necessary, a new buffer is alloced and
// put in pchByteSwap. Either way, 'pv' points
// to the correct string.
PBSInPlaceAlloc( (WCHAR**) &pv, (WCHAR**) &pchByteSwap, pstatus ); if( !NT_SUCCESS(*pstatus) ) goto Exit; PROPASSERT(IsUnicodeString((WCHAR*)pv, cb));
// Convert the Unicode string from the property
// set to Ansi.
PrpConvertToMultiByte( (WCHAR const *) pv, cb, CP_ACP, // Use the system default codepage
&pchConvert, &cbcopy, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
// 'pv' always has the correct string.
pv = pchConvert; } else if (CodePage == CP_WINUNICODE) { // Both the BSTR is unicode in the property set,
// and must remain unicode in the PropVariant.
// But byte-swapping may still be necessary.
CBBYTESWAP( sizeof(WCHAR) ); }
#ifdef LITTLEENDIAN
PROPASSERT( IsOLECHARString((BSTR)pv, cbcopy )); #endif
} // if( (VT_VECTOR | VT_BSTR) == pvar->vt ...
// Special LPSTR pre-processing ... again, we might
// need to do a unicode/ansi conversion.
else if (pvar->vt == (VT_VECTOR | VT_LPSTR)) { // LPSTRs are always Ansi. If the string
// is Unicode in the propset, convert now.
if (CodePage == CP_WINUNICODE) { // If byte-swapping is necessary, the string from
// the propset must be swapped before it can be
// converted to MBCS. If such a conversion
// is necessary, a new buffer is alloced and
// put in pchByteSwap. Either way, 'pv' points
// to the correct string.
PBSInPlaceAlloc( (WCHAR**) &pv, (WCHAR**) &pchByteSwap, pstatus ); if( !NT_SUCCESS(*pstatus) ) goto Exit; PROPASSERT(IsUnicodeString((WCHAR*)pv, cb));
// Convert to Ansi.
PrpConvertToMultiByte( (WCHAR const *) pv, cb, CP_ACP, // Use the system default codepage
&pchConvert, &cbcopy, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
pv = pchConvert; }
PROPASSERT( IsAnsiString( (CHAR const *)pv, cbcopy )); } // else if (pvar->vt == (VT_VECTOR | VT_LPSTR))
// Allocate memory in the PropVariant and copy
// the string.
if( (VT_BSTR | VT_VECTOR) == pvar->vt || (VT_BSTR | VT_ARRAY) == pvar->vt ) {
// For BSTRs, the allocate/copy is performed
// by SysStringAlloc. We've already verified
// that the string pointed to by pv is properly
// terminated.
*ppv = PrivSysAllocString( (BSTR) pv ); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory for BSTR element"); goto Exit; }
// The BSTR length should be the property length
// minus the NULL.
PROPASSERT( BSTRLEN(*ppv) == cbcopy - sizeof(OLECHAR) );
} // if( VT_BSTR == pvar->vt )
else { // For everything that's not a BSTR,
// Allocate a buffer in the PropVariant
*ppv = pma->Allocate(max(1, cbcopy)); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "StgConvertPropertyToVariant: no memory for string element"); goto Exit; }
// And then copy from the propset buffer to the PropVariant
RtlCopyMemory(*ppv, pv, cbcopy);
} // if( VT_BSTR == pvar->vt ) ... else
// If necessary, byte-swap in the PropVariant to get
// the proper byte-ordering.
PBSBuffer( *ppv, cbcopy, cbByteSwap );
// Adjust the PropVar element ptr to user-space (kernel only)
ADJUSTPOINTER(*ppv, PointerDelta, VOID *);
// Move, within the propset buffer, to the
// next element in the vector.
pbsrc += DwordAlign(cb);
// Delete the temporary buffers
CoTaskMemFree( pchByteSwap ); pchByteSwap = NULL;
CoTaskMemFree( pchConvert ); pchConvert = NULL;
} // if (cb != 0)
// Move, within the PropVariant, to the next
// element in the vector.
ppv++;
} // while (cElems-- > 0)
} // else if (pvar->vt == (VT_VECTOR | VT_CF)) ... else
} // if (!fPostAllocInit) ... else
ADJUSTPOINTER(*ppvK, PointerDelta, VOID *);
} // if (cb == 0) ... else
} // if (pv != NULL) ... else if (ppv != NULL)
Exit:
if( fSafeArrayLocked ) { PROPASSERT( NULL != pvSafeArrayData ); PrivSafeArrayUnaccessData( psa ); }
CoTaskMemFree( pchByteSwap ); CoTaskMemFree( pchConvert );
return(fIndirect); }
//+---------------------------------------------------------------------------
// Function: CleanupVariants, private
//
// Synopsis: Free all memory used by an array of PROPVARIANT
//
// Arguments: [pvar] -- pointer to PROPVARIANT
// [cprop] -- property count
// [pma] -- caller's memory free routine
//
// Returns: None
//---------------------------------------------------------------------------
VOID CleanupVariants( IN PROPVARIANT *pvar, IN ULONG cprop, IN PMemoryAllocator *pma) { // We can get null if we're called recursively (for a vector),
// and the vector property never got fully set up (i.e. the
// second buffer never got allocated).
if( NULL == pvar ) return;
while (cprop-- > 0) { VOID *pv = NULL; VOID **ppv = NULL; ULONG cElems = 0;
switch (pvar->vt) { case VT_CF: pv = pvar->pclipdata; if (pv != NULL && pvar->pclipdata->pClipData) { pma->Free(pvar->pclipdata->pClipData); } break;
case VT_VERSIONED_STREAM: pv = pvar->pVersionedStream; if( NULL != pv && NULL != pvar->pVersionedStream->pStream ) { pma->Free(pvar->pVersionedStream->pStream); } break;
case VT_BLOB: case VT_BLOB_OBJECT: pv = pvar->blob.pBlobData; break;
case VT_BSTR: case VT_CLSID: case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT: case VT_LPSTR: case VT_LPWSTR: AssertStringField(puuid); // VT_CLSID
AssertStringField(pszVal); // VT_STREAM, VT_STREAMED_OBJECT
AssertStringField(pszVal); // VT_STORAGE, VT_STORED_OBJECT
AssertStringField(bstrVal); // VT_BSTR
AssertStringField(pszVal); // VT_LPSTR
AssertStringField(pwszVal); // VT_LPWSTR
pv = pvar->pszVal; break;
// Vector properties:
case VT_VECTOR | VT_I1: case VT_VECTOR | VT_UI1: case VT_VECTOR | VT_I2: case VT_VECTOR | VT_UI2: case VT_VECTOR | VT_BOOL: case VT_VECTOR | VT_I4: case VT_VECTOR | VT_UI4: case VT_VECTOR | VT_R4: case VT_VECTOR | VT_ERROR: case VT_VECTOR | VT_I8: case VT_VECTOR | VT_UI8: case VT_VECTOR | VT_R8: case VT_VECTOR | VT_CY: case VT_VECTOR | VT_DATE: case VT_VECTOR | VT_FILETIME: case VT_VECTOR | VT_CLSID: AssertByteVector(cac); // VT_I1
AssertByteVector(caub); // VT_UI1
AssertShortVector(cai); // VT_I2
AssertShortVector(caui); // VT_UI2
AssertShortVector(cabool); // VT_BOOL
AssertLongVector(cal); // VT_I4
AssertLongVector(caul); // VT_UI4
AssertLongVector(caflt); // VT_R4
AssertLongVector(cascode); // VT_ERROR
AssertLongLongVector(cah); // VT_I8
AssertLongLongVector(cauh); // VT_UI8
AssertLongLongVector(cadbl); // VT_R8
AssertLongLongVector(cacy); // VT_CY
AssertLongLongVector(cadate); // VT_DATE
AssertLongLongVector(cafiletime); // VT_FILETIME
AssertVarVector(cauuid, sizeof(GUID)); // VT_CLSID
pv = pvar->cai.pElems; break;
case VT_VECTOR | VT_CF: { CLIPDATA *pcd;
cElems = pvar->caclipdata.cElems; pv = pcd = pvar->caclipdata.pElems; while (cElems-- > 0) { if (pcd->pClipData != NULL) { pma->Free(pcd->pClipData); } pcd++; } } break;
case VT_VECTOR | VT_BSTR: case VT_VECTOR | VT_LPSTR: case VT_VECTOR | VT_LPWSTR: AssertStringVector(cabstr); // VT_BSTR
AssertStringVector(calpstr); // VT_LPSTR
AssertStringVector(calpwstr); // VT_LPWSTR
cElems = pvar->calpstr.cElems; ppv = (VOID **) pvar->calpstr.pElems; break;
case VT_VECTOR | VT_VARIANT: CleanupVariants( pvar->capropvar.pElems, pvar->capropvar.cElems, pma); pv = pvar->capropvar.pElems; break;
} // switch (pvar->vt)
if (ppv != NULL) // STRING VECTOR property
{ // Save the vector of pointers
pv = (VOID *) ppv;
// Free the vector elements
while (cElems-- > 0) { if (*ppv != NULL) { if( (VT_BSTR | VT_VECTOR) == pvar->vt ) { PrivSysFreeString( (BSTR) *ppv ); } else { pma->Free((BYTE *) *ppv); } } ppv++; }
// Free the vector of pointers.
pma->Free(pv); pv = NULL;
} // if (ppv != NULL)
if (pv != NULL) { if( VT_BSTR == pvar->vt ) { PrivSysFreeString( (BSTR) pv ); } else { pma->Free((BYTE *) pv); } }
pvar->vt = VT_EMPTY;
// Move on to the next PropVar in the vector.
pvar++;
} // while (cprop-- > 0)
}
//+--------------------------------------------------------------------------
// Function: PropertyLength
//
// Synopsis: compute the length of a property including the variant type
//
// Arguments: [pprop] -- property value
// [cbbuf] -- max length of accessible memory at pprop
// [flags] -- CPropertySetStream flags
// [pstatus] -- pointer to NTSTATUS code
//
// Returns: length of property
//---------------------------------------------------------------------------
// First, define a wrapper for this function which returns errors
// using NT Exception Handling, rather than returning an NTSTATUS.
#if defined(WINNT) && !defined(IPROPERTY_DLL)
ULONG PropertyLength( SERIALIZEDPROPERTYVALUE const *pprop, ULONG cbbuf, BYTE flags) { NTSTATUS status; ULONG ulRet; ulRet = PropertyLengthNoEH( pprop, cbbuf, flags, &status ); if (!NT_SUCCESS( status )) RtlRaiseStatus( status );
return( ulRet ); }
#endif // #if defined(WINNT) && !defined(IPROPERTY_DLL)
// Now define the body of the function, returning errors with an
// NTSTATUS value instead of raising.
ULONG PropertyLengthNoEH( SERIALIZEDPROPERTYVALUE const *pprop, ULONG cbbuf, BYTE flags, OUT NTSTATUS *pstatus) { ULONG const *pl = (ULONG const *) pprop->rgb; ULONG cElems = 1; ULONG cSafeArrayDimensions = 1; ULONG cbremain = cbbuf; ULONG cb = 0, cbch; BOOLEAN fIllegalType = FALSE; const SAFEARRAYBOUND *rgsaBounds = NULL; ULONG vt;
*pstatus = STATUS_SUCCESS;
// Can we read the VT?
if (cbremain < CB_SERIALIZEDPROPERTYVALUE) { StatusOverflow(pstatus, "PropertyLength: dwType"); goto Exit; }
// Get the VT
vt = PropByteSwap( pprop->dwType ); cbremain -= CB_SERIALIZEDPROPERTYVALUE; PROPASSERT( sizeof(pprop->dwType) == CB_SERIALIZEDPROPERTYVALUE );
// If this is a vector or array, get the element count
if( VT_VECTOR & vt ) { // It's a vector.
// Can we read the element count?
if (cbremain < sizeof(ULONG)) { StatusOverflow(pstatus, "PropertyLength: cElems"); goto Exit; }
// Get the element count
cElems = PropByteSwap( *pl++ ); cbremain -= sizeof(ULONG); } else if( VT_ARRAY & vt ) { // It's an array
ULONG cbBounds = 0;
// Can we read the element VT and dimension count?
// (This VT is for the members of the array, as opposed
// to the VT_ARRAY in pprop->dwType).
if( sizeof(DWORD) + sizeof(UINT) > cbremain ) { StatusOverflow(pstatus, "PropertyLength: vt/cDims" ); goto Exit; }
// Read the SafeArray's VT (so we'll now ignore pprop->dwType)
vt = VT_ARRAY | PropByteSwap( *pl++ ); PROPASSERT( sizeof(DWORD) == sizeof(*pl) );
// Read the dimension count
cSafeArrayDimensions = PropByteSwap( *pl++ ); PROPASSERT( sizeof(DWORD) == sizeof(*pl) );
// Update the remaining count to be the bytes after the vt/count
cbremain -= sizeof(DWORD) + sizeof(UINT);
// Can we read the bounds?
cbBounds = sizeof(SAFEARRAYBOUND) * cSafeArrayDimensions; if( cbBounds > cbremain ) { StatusOverflow(pstatus, "PropertyLength: safearray bounds" ); goto Exit; }
// Get the bounds (point to them directly in the pprop)
cbremain -= cbBounds; rgsaBounds = reinterpret_cast<const SAFEARRAYBOUND *>(pl); pl = static_cast<const ULONG*>(Add2ConstPtr( pl, cbBounds ));
// Finally, calc the element count
cElems = CalcSafeArrayElements( cSafeArrayDimensions, rgsaBounds ); }
// Is this a vector/array of variants? If so, we need to make recursive
// calls to size the elements.
if( (VT_VECTOR | VT_VARIANT) == vt || (VT_ARRAY | VT_VARIANT) == vt ) { while (cElems-- > 0) { cb = PropertyLengthNoEH( (SERIALIZEDPROPERTYVALUE const *) pl, cbremain, flags | CPSS_VARIANTVECTOR, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; pl = (ULONG const *) Add2ConstPtr(pl, cb); cbremain -= cb; } }
// Otherwise (it's not a vector/array), we have to look at each
// type individually.
else { // Assume that characters are Unicode
// (we'll update this if it's not true).
cbch = sizeof(WCHAR);
// Switch over the raw type (i.e., mask out vt_vector, vt_array, etc.)
// We won't see VT_VARIANT here because we handled it in the if above.
// If we're in a recursive call, CPSS_VARIANTVECTOR will be true, and
// consequently certain types will be illegal (such as VT_NULL - you can't
// have this type in a vector|variant). Mostly, this switch updates
// pl to point just beyond the data - the total size in the end will be
// the delta between this and pprop. Note that we don't actually try
// to dereference pl, because it may be pointing off the end of the buffer
// (i.e., we don't check cbremain during the switch).
switch( VT_TYPEMASK & vt ) { case VT_EMPTY: case VT_NULL: fIllegalType = (flags & CPSS_VARIANTVECTOR) != 0; break;
case VT_I1: case VT_UI1: pl = (ULONG const *) Add2ConstPtr(pl, DwordAlign(cElems * sizeof(BYTE))); break;
case VT_I2: case VT_UI2: case VT_BOOL: pl = (ULONG const *) Add2ConstPtr(pl, DwordAlign(cElems * sizeof(USHORT))); break;
case VT_I4: case VT_INT: case VT_UI4: case VT_UINT: case VT_R4: case VT_ERROR: pl = (ULONG const *) Add2ConstPtr(pl, cElems * sizeof(ULONG)); break;
case VT_I8: case VT_UI8: case VT_R8: case VT_CY: case VT_DATE: case VT_FILETIME: pl = (ULONG const *) Add2ConstPtr(pl, cElems * sizeof(LONGLONG)); break;
case VT_CLSID: pl = (ULONG const *) Add2ConstPtr(pl, cElems * sizeof(GUID)); break;
case VT_DECIMAL: pl = (ULONG const *) Add2ConstPtr( pl, cElems * sizeof(DECIMAL) ); break;
case VT_VERSIONED_STREAM:
// Ensure we can read the GUID & string length
if( cbremain < sizeof(GUID) + sizeof(ULONG) ) { StatusOverflow(pstatus, "PropertyLength: VersionedStream" ); goto Exit; }
// Point to the string's length
pl = reinterpret_cast<const ULONG*>( Add2ConstPtr( pl, sizeof(GUID) ));
// Point past the end of the property
pl = reinterpret_cast<const ULONG*>( Add2ConstPtr( pl, sizeof(ULONG) + DwordAlign(PropByteSwap(*pl)) ));
break;
case VT_BLOB: case VT_BLOB_OBJECT: // FALLTHROUGH
case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT:
// These are stored as strings (which are the name of a
// stream/storage).
if (flags & CPSS_VARIANTVECTOR) { fIllegalType = TRUE; break; } // FALLTHROUGH
case VT_CF: case VT_BSTR: case VT_LPSTR:
// This have byte counts, not character counts
cbch = sizeof(BYTE); // FALLTHROUGH
case VT_LPWSTR:
// Handle all the length-prefixed types
while (cElems-- > 0) { if (cbremain < sizeof(ULONG) || cbremain < (cb = sizeof(ULONG) + DwordAlign(PropByteSwap(*pl) * cbch))) { StatusOverflow(pstatus, "PropertyLength: String/BLOB/CF/Indirect"); goto Exit; }
#ifdef LITTLEENDIAN
PROPASSERT( (PropByteSwap(pprop->dwType) & VT_TYPEMASK) != VT_LPWSTR || IsUnicodeString( (WCHAR const *) &pl[1], PropByteSwap(*pl) * sizeof(WCHAR))); #endif
pl = (ULONG const *) Add2ConstPtr(pl, cb); cbremain -= cb; } break;
default:
fIllegalType = TRUE; break;
} // switch( VT_TYPEMASK & vt )
}
// Did we get an illegal type (e.g. a VT_STREAM within a CPSS_VARIANTVECTOR)?
if (fIllegalType) { propDbg(( DEB_IWARN, "PropertyLength: Unsupported VarType (0x%x)\n", vt )); *pstatus = STATUS_NOT_SUPPORTED; goto Exit; }
// Calculate the final cb and verify it.
cb = (ULONG) ((BYTE *) pl - (BYTE *) pprop);
if (cbbuf < cb) { StatusOverflow(pstatus, "PropertyLength: cb"); goto Exit; }
// Make sure PropertyLength works when limited to an exact size buffer.
PROPASSERT(cb == cbbuf || PropertyLengthNoEH(pprop, cb, flags, pstatus) == cb);
// ----
// Exit
// ----
Exit:
// Normalize the error return value.
if( !NT_SUCCESS(*pstatus) ) cb = 0;
return(cb);
} // PropertyLengthNoEH()
//+--------------------------------------------------------------------------
// Function: StgPropertyLengthAsVariant
//
// Synopsis: compute the size of external memory required to store the
// property as a PROPVARIANT
//
// Arguments: [pprop] -- property value
// [cbprop] -- computed length of pprop in propset stream
// [CodePage] -- property set codepage
// [flags] -- CPropertySetStream flags
// [pstatus] -- pointer to NTSTATUS code
//
// Returns: length of property
//---------------------------------------------------------------------------
#if defined(WINNT)
// First, define a wrapper which raises NT Exceptions for compatibility
// with older callers who expect it.
EXTERN_C ULONG __stdcall StgPropertyLengthAsVariant( IN SERIALIZEDPROPERTYVALUE const *pprop, IN ULONG cbprop, IN USHORT CodePage, IN BYTE flags) { NTSTATUS status; ULONG ulRet; ulRet = StgPropertyLengthAsVariantNoEH( pprop, cbprop, CodePage, flags, &status ); if (!NT_SUCCESS( status )) RtlRaiseStatus( status );
return( ulRet ); }
// Now define the body of the function, returning errors with an
// NTSTATUS value instead of raising.
ULONG StgPropertyLengthAsVariantNoEH( IN SERIALIZEDPROPERTYVALUE const *pprop, IN ULONG cbprop, IN USHORT CodePage, IN BYTE flags, OUT NTSTATUS *pstatus) { ULONG cElems = 0; ULONG cbvar = 0; const ULONG *pl = reinterpret_cast<const ULONG*>(pprop->rgb);
*pstatus = STATUS_SUCCESS;
PROPASSERT(cbprop == PropertyLengthNoEH(pprop, cbprop, flags, pstatus)); if( VT_VECTOR & PropByteSwap(pprop->dwType) ) { if( VT_ARRAY & PropByteSwap(pprop->dwType) ) { StatusInvalidParameter( pstatus, "Both Array and Vector bits set" ); goto Exit; } cElems = *(ULONG *) pprop->rgb; pl++; cbprop -= sizeof(ULONG); // Discount the element count
} else if( VT_ARRAY & PropByteSwap(pprop->dwType) ) { const SAFEARRAYBOUND *rgsaBounds = NULL; ULONG cDims = 0; VARTYPE vtInternal;
if( VT_VECTOR & PropByteSwap(pprop->dwType) ) { StatusInvalidParameter( pstatus, "Both Array and Vector bits set" ); goto Exit; }
vtInternal = static_cast<VARTYPE>(*pl++); cDims = *pl++; PROPASSERT( sizeof(UINT) == sizeof(LONG) ); rgsaBounds = reinterpret_cast<const SAFEARRAYBOUND*>(pl); pl = static_cast<const ULONG*>( Add2ConstPtr( pl, cDims * sizeof(SAFEARRAYBOUND) )); cElems = CalcSafeArrayElements( cDims, rgsaBounds );
// Adjust cbprop to take into account that we have to create a SafeArray
cbprop = cbprop - sizeof(DWORD) // vtInternal
- sizeof(UINT) // cDims
+ sizeof(SAFEARRAY) // The SafeArray that will be alloced
+ sizeof(GUID) // hidden extra data alloc-ed with a safearray
- sizeof(SAFEARRAYBOUND); // Discount SAFEARRAY.rgsabound[1]
}
switch( PropByteSwap(pprop->dwType) ) { // We don't need to check for VT_BYREF, becuase serialized property sets
// never contain them.
//default:
//case VT_EMPTY:
//case VT_NULL:
//case VT_I1:
//case VT_UI1:
//case VT_I2:
//case VT_UI2:
//case VT_BOOL:
//case VT_INT:
//case VT_UINT:
//case VT_I4:
//case VT_UI4:
//case VT_R4:
//case VT_ERROR:
//case VT_I8:
//case VT_UI8:
//case VT_R8:
//case VT_CY:
//case VT_DATE:
//case VT_FILETIME:
//case VT_DECIMAL:
//cbvar = 0;
//break;
case VT_CLSID: cbvar = cbprop - sizeof(ULONG); // don't include VARTYPE
break;
// VT_CF: Round CLIPDATA up to Quad boundary, then drop VARTYPE+size+
// clipfmt, which get tossed or unmarshalled into CLIPDATA. Round
// byte-granular data size to a Quad boundary when returning result.
case VT_CF: cbvar = QuadAlign(sizeof(CLIPDATA)) + cbprop - 3 * sizeof(ULONG); break;
case VT_BLOB: case VT_BLOB_OBJECT: cbvar = cbprop - 2 * sizeof(ULONG); // don't include VARTYPE & size
break;
case VT_VERSIONED_STREAM: case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT:
cbvar = cbprop - 2 * sizeof(ULONG); // don't include VARTYPE & size
if (CodePage != CP_WINUNICODE) { cbvar *= sizeof(WCHAR); // worst case Unicode conversion
}
break;
case VT_BSTR:
// Don't include the size of the VT field, but leave
// the size of the length field accounted for.
cbvar = cbprop - sizeof(ULONG);
// Worst-case Ansi->Unicode conversion:
cbvar *= sizeof(OLECHAR);
break;
case VT_LPSTR: // Assume Ansi conversion saves no space
case VT_LPWSTR: cbvar = cbprop - 2 * sizeof(ULONG); break;
case VT_ARRAY | VT_I1: case VT_ARRAY | VT_UI1: case VT_ARRAY | VT_I2: case VT_ARRAY | VT_UI2: case VT_ARRAY | VT_BOOL: case VT_ARRAY | VT_I4: case VT_ARRAY | VT_UI4: case VT_ARRAY | VT_INT: case VT_ARRAY | VT_UINT: case VT_ARRAY | VT_R4: case VT_ARRAY | VT_ERROR: case VT_ARRAY | VT_DECIMAL: //case VT_ARRAY | VT_I8:
//case VT_ARRAY | VT_UI8:
case VT_ARRAY | VT_R8: case VT_ARRAY | VT_CY: case VT_ARRAY | VT_DATE:
// don't include VARTYPE field
cbvar = cbprop - sizeof(ULONG); break;
// Vector properties:
case VT_VECTOR | VT_I1: case VT_VECTOR | VT_UI1: case VT_VECTOR | VT_I2: case VT_VECTOR | VT_UI2: case VT_VECTOR | VT_BOOL: case VT_VECTOR | VT_I4: case VT_VECTOR | VT_UI4: case VT_VECTOR | VT_R4: case VT_VECTOR | VT_ERROR: case VT_VECTOR | VT_I8: case VT_VECTOR | VT_UI8: case VT_VECTOR | VT_R8: case VT_VECTOR | VT_CY: case VT_VECTOR | VT_DATE: case VT_VECTOR | VT_FILETIME: case VT_VECTOR | VT_CLSID: AssertByteVector(cac); // VT_I1
AssertByteVector(caub); // VT_UI1
AssertShortVector(cai); // VT_I2
AssertShortVector(caui); // VT_UI2
AssertShortVector(cabool); // VT_BOOL
AssertLongVector(cal); // VT_I4
AssertLongVector(caul); // VT_UI4
AssertLongVector(caflt); // VT_R4
AssertLongVector(cascode); // VT_ERROR
AssertLongLongVector(cah); // VT_I8
AssertLongLongVector(cauh); // VT_UI8
AssertLongLongVector(cadbl); // VT_R8
AssertLongLongVector(cacy); // VT_CY
AssertLongLongVector(cadate); // VT_DATE
AssertLongLongVector(cafiletime); // VT_FILETIME
AssertVarVector(cauuid, sizeof(GUID));
// don't include VARTYPE and count fields
cbvar = cbprop - 2 * sizeof(ULONG); break;
case VT_VECTOR | VT_CF: // add room for each pointer
AssertVarVector(caclipdata, sizeof(CLIPDATA)); // VT_CF
// don't include VARTYPE and count fields
cbvar = cbprop - 2 * sizeof(ULONG);
// add room for each CLIPDATA data pointer and enough to Quad align
// every clipdata data element and 1 ULONG to Quad align the
// CLIPDATA array
cbvar += cElems * (sizeof(BYTE *) + sizeof(ULONG)) + sizeof(ULONG); break;
case VT_VECTOR | VT_BSTR: // add room for each BSTRLEN
case VT_ARRAY | VT_BSTR: AssertStringVector(cabstr); // VT_BSTR
//Assert
// don't include VARTYPE field
cbvar = cbprop - sizeof(ULONG);
// For vectors, don't include the count field
if( VT_VECTOR & PropByteSwap(pprop->dwType) ) cbvar -= sizeof(ULONG); if (CodePage != CP_WINUNICODE) { cbvar *= sizeof(OLECHAR); // worst case Unicode conversion
}
// add room for each BSTRLEN value and enough to Quad align
// every BSTR and 1 ULONG to Quad align the array of BSTR pointers.
cbvar += cElems * (sizeof(ULONG) + sizeof(ULONG)) + sizeof(ULONG); break;
case VT_VECTOR | VT_LPSTR: // Assume Ansi conversion saves no space
case VT_VECTOR | VT_LPWSTR: AssertStringVector(calpstr); // VT_LPSTR
AssertStringVector(calpwstr); // VT_LPWSTR
// don't include VARTYPE and count fields
cbvar = cbprop - 2 * sizeof(ULONG);
// add enough room to Quadalign the array of string pointers.
cbvar += cElems * sizeof(ULONG) + sizeof(ULONG); break;
case VT_VECTOR | VT_VARIANT: case VT_ARRAY | VT_VARIANT: { ULONG cbremain = cbprop - sizeof(ULONG); // Discount the VT
cbvar = cElems * sizeof(PROPVARIANT);
while (cElems-- > 0) { ULONG cbpropElem; ULONG cbvarElem; cbpropElem = PropertyLengthNoEH( (SERIALIZEDPROPERTYVALUE *) pl, cbremain, flags | CPSS_VARIANTVECTOR, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
cbvarElem = StgPropertyLengthAsVariantNoEH( (SERIALIZEDPROPERTYVALUE *) pl, cbpropElem, CodePage, flags | CPSS_VARIANTVECTOR, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit;
pl = (ULONG const *) Add2ConstPtr(pl, cbpropElem); cbremain -= cbpropElem; cbvar += cbvarElem; } break; } }
// ----
// Exit
// ----
Exit:
// Normalize the error return value.
if( !NT_SUCCESS(*pstatus) ) cbvar = 0;
return(QuadAlign(cbvar));
} // StgPropertyLengthAsVariantNoEH
#endif // #if defined(WINNT)
//+--------------------------------------------------------------------------
// Function: PBSCopy
//
// Synopsis: This is a Property Byte-Swap routine. The PBS routines
// only compile in the BIGENDIAN build. In the
// LITTLEENDIAN build, they are inlined with NOOP functions.
//
// This routine copies the source to the destination,
// byte-swapping as it copies.
//
// Arguments: [VOID*] pvDest
// Pointer to the target (swapped) buffer.
// This must be pre-allocated by the caller.
// [VOID*] pvSource
// Pointer to the original buffer.
// [ULONG] cbSize
// Size in bytes of the buffer.
// [ULONG] cbByteSwap
// Size of byte-swapping units.
//
// Returns: None.
//
//---------------------------------------------------------------------------
#ifdef BIGENDIAN
VOID PBSCopy( OUT VOID *pvDest, IN VOID const *pvSource, IN ULONG cbCopy, IN LONG cbByteSwap ) { PROPASSERT( (cbCopy & 1) == 0 ); PROPASSERT( pvDest != NULL && pvSource != NULL );
memcpy( pvDest, pvSource, cbCopy ); PBSBuffer( pvDest, cbCopy, cbByteSwap ); }
#endif // BIGENDIAN
//+--------------------------------------------------------------------------
// Function: PBSAllocAndCopy
//
// Synopsis: This is a Property Byte-Swap routine. The PBS routines
// only compile in the BIGENDIAN build. In the
// LITTLEENDIAN build, they are inlined with NOOP functions.
//
// This routine allocs a buffer, and swaps the bytes from
// the source buffer into the destination.
//
// Arguments: [VOID**] ppvDest (out)
// On success will point to the swapped buffer.
// [VOID*] pvSource (in)
// Pointer to the original buffer.
// [ULONG] cbSize (in)
// Size in bytes of the buffer.
// [LONG] cbByteSwap (in)
// Size of byte-swapping units.
// [NTSTATUS*] pstatus (out)
// NTSTATUS code.
//
// Returns: None.
//
// Note: The caller is responsible for freeing *ppvDest
// (using ::delete).
//
//---------------------------------------------------------------------------
#ifdef BIGENDIAN
VOID PBSAllocAndCopy( OUT VOID **ppvDest, IN VOID const *pvSource, ULONG cbSize, LONG cbByteSwap, OUT NTSTATUS *pstatus) { // -----
// Begin
// -----
*pstatus = STATUS_SUCCESS; PROPASSERT( ppvDest != NULL && pvSource != NULL );
// Allocate a buffer.
*ppvDest = CoTaskMemAlloc( cbSize ); if( NULL == *ppvDest ) { *pstatus = STATUS_NO_MEMORY; goto Exit; }
// Swap/copy the bytes.
PBSCopy( *ppvDest, pvSource, cbSize, cbByteSwap );
// ----
// Exit
// ----
Exit:
return;
} // PBSAllocAndCopy
#endif // BIGENDIAN
//+--------------------------------------------------------------------------
// Function: PBSInPlaceAlloc
//
// Synopsis: This is a Property Byte-Swap routine. The PBS routines
// only compile in the BIGENDIAN build. In the
// LITTLEENDIAN build, they are inlined with NOOP functions.
//
// This routine takes a WCHAR array, allocates a new buffer,
// and swaps the original array into the new buffer.
//
//
// Arguments: [WCHAR**] ppwszResult
// IN: *ppwszResult points to string to be swapped.
// OUT: *ppwszResult points to the swapped string.
// [WCHAR**] ppwszBuffer
// *ppwszBuffer points to the buffer which was allocated
// for the swapped bytes (should be the same as *ppwszResult).
// *ppwszBuffer must be NULL on input, and must be freed
// by the caller (using ::delete).
// [NTSTATUS*] pstatus
// NTSTATUS code.
//
// Returns: None.
//
// On input, *ppwszResult contains the original string.
// An equivalently sized buffer is allocated in *ppwszBuffer,
// and *ppwszResult is byte-swapped into it. *ppwszResult
// is then set to the new *ppwszBuffer.
//
// It doesn't appear to useful to have both buffer parameters,
// but it makes it easier on the caller in certain circumstances;
// *ppwszResult always points to the correct string, whether the
// build is BIGENDIAN (alloc & swap takes place) or the build
// is LITTLEENDIAN (nothing happes, so *ppwszResult continues
// to point to the proper string). The LITTLEENDIAN version of
// this function is implemented as an inline routine.
//
//---------------------------------------------------------------------------
#ifdef BIGENDIAN
VOID PBSInPlaceAlloc( IN OUT WCHAR** ppwszResult, OUT WCHAR** ppwszBuffer, OUT NTSTATUS *pstatus ) { // ------
// Locals
// ------
WCHAR *pwszNewBuffer;
// Pointers which will walk through the input buffers.
WCHAR *pwszOriginal, *pwszSwapped;
// -----
// Begin
// -----
*pstatus = STATUS_SUCCESS;
// Allocate a new buffer.
pwszNewBuffer = CoTaskMemAlloc( sizeof(WCHAR)*( Prop_wcslen(*ppwszResult) + 1 )); if( NULL == pwszNewBuffer ) { *pstatus = STATUS_NO_MEMORY; goto Exit; }
// Swap the WCHARs into the new buffer.
pwszOriginal = *ppwszResult; pwszSwapped = pwszNewBuffer;
do { *pwszSwapped = PropByteSwap(*pwszOriginal++); } while( *pwszSwapped++ != L'\0' );
// If the caller wants a special pointer to the new buffer,
// set it now.
if( NULL != ppwszBuffer ) { PROPASSERT( NULL== *ppwszBuffer ); *ppwszBuffer = pwszNewBuffer; }
// Also point *ppwszResult to the new buffer.
*ppwszResult = pwszNewBuffer;
// ----
// Exit
// ----
Exit: return; } // PropByteSwap( WCHAR**, WCHAR**, NTSTATUS*)
#endif // BIGENDIAN
//+--------------------------------------------------------------------------
// Function: PBSBuffer
//
// Synopsis: This is a Property Byte-Swap routine. The PBS routines
// only compile in the BIGENDIAN build. In the
// LITTLEENDIAN build, they are inlined with NOOP functions.
//
// This routine takes a buffer and byte-swaps it. The caller
// specifies the size of the buffer, and the granularity of
// the byte-swapping.
//
// Arguments: [VOID*] pv
// Pointer to the buffer to be swapped.
// [ULONG] cbSize
// Size in bytes of the buffer.
// [ULONG] cbByteSwap
// Size of byte-swapping units.
//
// Returns: None.
//
// For example, an array of 4 WORDs could be swapped with:
//
// PBSBuffer( (VOID*) aw, 8, sizeof(WORD) );
//
//---------------------------------------------------------------------------
#ifdef BIGENDIAN
VOID PBSBuffer( IN OUT VOID *pv, IN ULONG cbSize, IN ULONG cbByteSwap ) { ULONG ulIndex;
// What kind of swapping should be do?
switch( cbByteSwap ) { // No swapping required
case 0: case( sizeof(BYTE) ):
// Nothing to do.
break;
// Swap WORDs
case( sizeof(WORD) ): for( ulIndex = 0; ulIndex < cbSize/sizeof(WORD); ulIndex++ ) ByteSwap( &((WORD*)pv)[ulIndex] ); break;
// Swap DWORDs
case( sizeof(DWORD) ):
for( ulIndex = 0; ulIndex < cbSize/sizeof(DWORD); ulIndex++ ) ByteSwap( &((DWORD*)pv)[ulIndex] ); break;
// Swap LONGLONGs
case( sizeof(LONGLONG) ):
for( ulIndex = 0; ulIndex < cbSize/sizeof(LONGLONG); ulIndex++ ) ByteSwap( &((LONGLONG*)pv)[ulIndex] ); break;
// Swap GUIDs
case CBBYTESWAP_UID:
for( ulIndex = 0; ulIndex < cbSize/sizeof(GUID); ulIndex++ ) ByteSwap( &((GUID*)pv)[ulIndex] ); break;
// Error
default: PROPASSERT( !"Invalid generic byte-swap size" ); } } // PropByteSwap( VOID*, ULONG, ULONG )
#endif // BIGENDIAN
|