//+-------------------------------------------------------------------------- // // 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 #include #ifndef _MAC #include // 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( 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(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(psa), ulIndex, &lLowerBound ))) { status = PrivSafeArrayGetUBound( const_cast(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(parray), const_cast(&parraydata) ); if( FAILED(*pstatus) ) goto Exit; fSafeArrayLocked = TRUE; pv = parraydata; ppv = static_cast(pv); // Determine the dimension count and element size cSafeArrayDims = PrivSafeArrayGetDim( const_cast(parray) ); cb = PrivSafeArrayGetElemsize( const_cast(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(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(pbdst) = pvar->pVersionedStream->guidVersion; else *reinterpret_cast(pbdst) = GUID_NULL; PropByteSwap( reinterpret_cast(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(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(~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(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( 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( pprop->rgb ); PropByteSwap( &pvar->pVersionedStream->guidVersion ); // A buffer will be allocated and the stream name put into *ppv. ppv = reinterpret_cast( &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(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(pl); pl = static_cast(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( Add2ConstPtr( pl, sizeof(GUID) )); // Point past the end of the property pl = reinterpret_cast( 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(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(*pl++); cDims = *pl++; PROPASSERT( sizeof(UINT) == sizeof(LONG) ); rgsaBounds = reinterpret_cast(pl); pl = static_cast( 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