//+-------------------------------------------------------------------------- // // 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. // //--------------------------------------------------------------------------- #include #include #ifndef _MAC #include // for CP_WINUNICODE #endif #include "propvar.h" // These optionally-compiled directives tell the compiler & debugger // where the real file, rather than the copy, is located. #ifdef _ORIG_FILE_LOCATION_ #if __LINE__ != 40 #error File heading has change size #else #line 44 "\\nt\\private\\dcomidl\\propvar.cxx" #endif #endif #ifndef newk #define newk(Tag, pCounter) new #endif // The below variant types are supported in property set streams. In addition, // the variants found in an array of variants (VT_VECTOR | VT_VARIANT) can only // contain the types listed below as legal for arrays. Nested vectors of // VT_VARIANT are specifically *allowed*. // // dd xx symbolic name field size // -- --- ------------- ----- ---- // -1 - ffff VT_ILLEGAL can't legally be stored // // 0 - x00 VT_EMPTY 0 // 1 - x01 VT_NULL 0 // // 16 - x10 VT_I1 CHAR cVal sizeof(char) // 17 - x11 VT_UI1 UCHAR bVal sizeof(char) // // 2 - x02 VT_I2 short iVal sizeof(short) // 18 - x12 VT_UI2 USHORT uiVal sizeof(short) // 11 - x0b VT_BOOL VARIANT_BOOL boolVal sizeof(short) // // 3 - x03 VT_I4 long lVal sizeof(long) // 19 - x13 VT_UI4 ULONG ulVal sizeof(long) // 4 - x04 VT_R4 float fltVal sizeof(long) // 10 - x0a VT_ERROR SCODE scode sizeof(long) // // 20 - x14 VT_I8 LARGE_INTEGER hVal sizeof(ll) // 21 - x15 VT_UI8 ULARGE_INTEGER uhVal sizeof(ll) // 5 - x05 VT_R8 double dblVal sizeof(ll) // 6 - x06 VT_CY CY cyVal sizeof(ll) // 7 - x07 VT_DATE DATE date sizeof(ll) // 64 - x40 VT_FILETIME FILETIME filetime sizeof(ll) // // 72 - x48 VT_CLSID CLSID *puuid sizeof(GUID) // // 65 - x41 VT_BLOB BLOB blob counted array of bytes // 70 - x46 VT_BLOB_OBJECT BLOB blob counted array of bytes // 71 - x47 VT_CF CLIPDATA *pclipdata " + ulClipFmt // 66 - x42 VT_STREAM LPSTR pszVal counted array of bytes // 68 - x44 VT_STREAMED_OBJECT LPSTR pszVal counted array of bytes // 67 - x43 VT_STORAGE LPSTR pszVal counted array of bytes // 69 - x45 VT_STORED_OBJECT LPSTR pszVal counted array of bytes // 8 - x08 VT_BSTR BSTR bstrVal counted array of bytes // 30 - x1e VT_LPSTR LPSTR pszVal counted array of bytes // // 31 - x1f VT_LPWSTR LPWSTR pwszVal counted array of WCHARs // // x1010 VT_VECTOR | VT_I1 CAC cac cElems * sizeof(char) // x1011 VT_VECTOR | VT_UI1 CAUB caub cElems * sizeof(char) // // x1002 VT_VECTOR | VT_I2 CAI cai cElems * sizeof(short) // x1012 VT_VECTOR | VT_UI2 CAUI caui cElems * sizeof(short) // x100b VT_VECTOR | VT_BOOL CABOOL cabool cElems * sizeof(short) // // x1003 VT_VECTOR | VT_I4 CAL cal cElems * sizeof(long) // x1013 VT_VECTOR | VT_UI4 CAUL caul cElems * sizeof(long) // x1004 VT_VECTOR | VT_R4 CAFLT caflt cElems * sizeof(long) // x100a VT_VECTOR | VT_ERROR CAERROR cascode cElems * sizeof(long) // // x1014 VT_VECTOR | VT_I8 CAH cah cElems * sizeof(ll) // x1015 VT_VECTOR | VT_UI8 CAUH cauh cElems * sizeof(ll) // x1005 VT_VECTOR | VT_R8 CADBL cadbl cElems * sizeof(ll) // x1006 VT_VECTOR | VT_CY CACY cacy cElems * sizeof(ll) // x1007 VT_VECTOR | VT_DATE CADATE cadate cElems * sizeof(ll) // x1040 VT_VECTOR | VT_FILETIME CAFILETIME cafiletime cElems * sizeof(ll) // // x1048 VT_VECTOR | VT_CLSID CACLSID cauuid cElems * sizeof(GUID) // // x1047 VT_VECTOR | VT_CF CACLIPDATA caclipdata cElems cntarray of bytes // x1008 VT_VECTOR | VT_BSTR CABSTR cabstr cElems cntarray of bytes // x101e VT_VECTOR | VT_LPSTR CALPSTR calpstr cElems cntarray of bytes // // x101f VT_VECTOR | VT_LPWSTR CALPWSTR calpwstr cElems cntarray of WCHAR // // x100c VT_VECTOR | VT_VARIANT CAPROPVARIANT capropvar cElems variants // (recurse on each) //+--------------------------------------------------------------------------- // Function: RtlpConvertToUnicode, 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 RtlpConvertToUnicode( 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; PROPASSERT(UnicodeCallouts.pfnMultiByteToWideChar != NULL); pwszName = NULL; cwcName = 0; while (TRUE) { cwcName = (*UnicodeCallouts.pfnMultiByteToWideChar)( CodePage, 0, // dwFlags pch, cb, pwszName, cwcName); if (cwcName == 0) { delete [] pwszName; // If there was an error, assume that it was a code-page // incompatibility problem. StatusError(pstatus, "RtlpConvertToUnicode: MultiByteToWideChar error", STATUS_UNMAPPABLE_CHARACTER); goto Exit; } if (pwszName != NULL) { DebugTrace(0, DEBTRACE_PROPERTY, ( "RtlpConvertToUnicode: pch='%s'[%x] pwc='%ws'[%x->%x]\n", pch, cb, pwszName, *pcb, cwcName * sizeof(WCHAR))); break; } *pcb = cwcName * sizeof(WCHAR); *ppwc = pwszName = (WCHAR *) newk(mtPropSetStream, NULL) CHAR[*pcb]; if (pwszName == NULL) { StatusNoMemory(pstatus, "RtlpConvertToUnicode: no memory"); goto Exit; } } // ---- // Exit // ---- Exit: return; } //+--------------------------------------------------------------------------- // Function: RtlpConvertToMultiByte, 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 RtlpConvertToMultiByte( 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; PROPASSERT(UnicodeCallouts.pfnWideCharToMultiByte != NULL); pszName = NULL; cbName = 0; while (TRUE) { cbName = (*UnicodeCallouts.pfnWideCharToMultiByte)( CodePage, 0, // dwFlags pwc, cb/sizeof(WCHAR), pszName, cbName, NULL, // lpDefaultChar NULL); // lpUsedDefaultChar if (cbName == 0) { delete [] pszName; // If there was an error, assume that it was a code-page // incompatibility problem. StatusError(pstatus, "RtlpConvertToMultiByte: WideCharToMultiByte error", STATUS_UNMAPPABLE_CHARACTER); goto Exit; } if (pszName != NULL) { DebugTrace(0, DEBTRACE_PROPERTY, ( "RtlpConvertToMultiByte: pwc='%ws'[%x] pch='%s'[%x->%x]\n", pwc, cb, pszName, *pcb, cbName)); break; } *pcb = cbName; *ppch = pszName = newk(mtPropSetStream, NULL) CHAR[cbName]; if (pszName == NULL) { StatusNoMemory(pstatus, "RtlpConvertToMultiByte: no memory"); goto Exit; } } // ---- // Exit // ---- Exit: return; } //+--------------------------------------------------------------------------- // Function: RtlConvertVariantToProperty, 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) // [fVariantVector] -- 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. SERIALIZEDPROPERTYVALUE * RtlConvertVariantToProperty( IN PROPVARIANT const *pvar, IN USHORT CodePage, OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, IN OUT ULONG *pcb, IN PROPID pid, IN BOOLEAN fVariantVector, OPTIONAL OUT ULONG *pcIndirect) { SERIALIZEDPROPERTYVALUE *ppropRet; NTSTATUS status; ppropRet = RtlConvertVariantToPropertyNoEH( pvar, CodePage, pprop, pcb, pid, fVariantVector, pcIndirect, &status ); if (!NT_SUCCESS( status )) RtlRaiseStatus( status ); return (ppropRet ); } // Now define the body of the function, returning errors with an // NTSTATUS value instead of raising. SERIALIZEDPROPERTYVALUE * RtlConvertVariantToPropertyNoEH( IN PROPVARIANT const *pvar, IN USHORT CodePage, OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, IN OUT ULONG *pcb, IN PROPID pid, IN BOOLEAN fVariantVector, OPTIONAL OUT ULONG *pcIndirect, OUT NTSTATUS *pstatus) { *pstatus = STATUS_SUCCESS; // ------ // Locals // ------ CHAR *pchConvert = NULL; ULONG count; BYTE *pbdst; ULONG cbch = 0; ULONG cbchdiv = 0; ULONG cb = 0; // 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) ((pvar->vt & VT_VECTOR) != 0); BOOLEAN fIllegalType = FALSE; VOID **ppv; OLECHAR aocName[4 + 10 + 1]; // enough for "prop%lu" + L'\0' // ------------------------------------------------------- // 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 = fVariantVector; break; #ifdef PROPVAR_VT_I1 case VT_I1: AssertByteField(cVal); // VT_I1 #endif 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_UI4: case VT_R4: case VT_ERROR: AssertLongField(lVal); // VT_I4 AssertLongField(ulVal); // VT_UI4 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 each DWORD independently. 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 (64 bits). CBBYTESWAP(cb); break; case VT_CLSID: AssertStringField(puuid); // VT_CLSID cb = sizeof(GUID); pv = pvar->puuid; fCheckNullSource = TRUE; // If swapping, special handling is required. CBBYTESWAP( CBBYTESWAP_UID ); break; case VT_CF: // Validate the PropVariant if (pvar->pclipdata == NULL || pvar->pclipdata->cbSize < sizeof(pvar->pclipdata->ulClipFmt) ) { StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: 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 = fVariantVector; pcount = &pvar->blob.cbSize; cb = *pcount; pv = pvar->blob.pBlobData; fCheckNullSource = TRUE; // Note that no byte-swapping of 'pv' is necessary. break; case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT: if (fVariantVector) { fIllegalType = TRUE; break; } if (pcIndirect != NULL) { (*pcIndirect)++; PROPGENPROPERTYNAME(aocName, pid); pv = aocName; } else { PROPASSERT( pvar->pwszVal == NULL || IsUnicodeString(pvar->pwszVal, MAXULONG)); pv = pvar->pwszVal; } count = 1; // default to forcing an error on NULL pointer goto noansicheck; case VT_LPSTR: 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(pszVal); // VT_LPSTR // 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 = 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)); RtlpConvertToUnicode( (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 (pvar->vt == VT_BSTR) { count = BSTRLEN(pv); // Verify that the input BSTR is terminated. if (pvar->bstrVal[count/sizeof(OLECHAR)] != OLESTR('\0')) { PROPASSERT(pvar->bstrVal[count/sizeof(OLECHAR)] == OLESTR('\0')); StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: bad BSTR null char"); goto Exit; } // Increment the count to include the terminator. count += sizeof(OLECHAR); } else { count = (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 )); RtlpConvertToMultiByte( (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)); RtlpConvertToUnicode( (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 // BUGBUG: Create an IsByteSwappedUnicodeString routine 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)); pv = pvar->pwszVal; if (pv == NULL) { count = 0; fCheckNullSource = TRUE; } else { // Calculate the [length] field. count = 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; // Vector properties: #ifdef PROPVAR_VT_I1 case VT_VECTOR | VT_I1: AssertByteVector(cac); // VT_I1 #endif case VT_VECTOR | VT_UI1: AssertByteVector(caub); // VT_UI1 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 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 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 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 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)); 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: cbch = sizeof(CLIPDATA); cbchdiv = sizeof(BYTE); goto stringvector; case VT_VECTOR | VT_BSTR: case VT_VECTOR | VT_LPSTR: cbchdiv = cbch = sizeof(BYTE); goto stringvector; case VT_VECTOR | VT_LPWSTR: cbchdiv = cbch = sizeof(WCHAR); goto stringvector; case VT_VECTOR | VT_VARIANT: 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; default: DebugTrace(0, DEBTRACE_ERROR, ( "RtlConvertVariantToProperty: unsupported vt=%x\n", pvar->vt)); StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: bad type"); goto Exit; } // switch (pvar->vt) // 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) { StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: 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 a Vector of Strings/Variants/CFs? if (cbch != 0) { // Yes. ULONG cElems; PROPASSERT(pcount != NULL); PROPASSERT(*pcount == 0 || ppv != NULL); PROPASSERT(0 == cbByteSwap); // Start calculating the serialized size. Include the sizes // of the VT & element count. cb = sizeof(ULONG) + sizeof(ULONG); // Is this a Variant Vector? if (cbch != MAXULONG) { // No. Include each element's length field. cb += *pcount * sizeof(ULONG); } // Is there room in the caller's buffer for everything // counted so far? if (*pcb < cb) { // No - we won't serialize the data, but we will continue // to calculate cb. pprop = NULL; } // Write the count of vector elements. if (pprop != NULL) { *(ULONG *) pbdst = PropByteSwap((ULONG) *pcount); pbdst += sizeof(ULONG); } // Walk through the vector and write the elements. for (cElems = *pcount; cElems > 0; cElems--) { ULONG cbcopy = 0; // Switch on the size of the element. switch (cbch) { // // VT_VARIANT // case MAXULONG: cbcopy = MAXULONG; // Perform a recursive serialization RtlConvertVariantToPropertyNoEH( (PROPVARIANT *) ppv, CodePage, NULL, &cbcopy, PID_ILLEGAL, TRUE, NULL, 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, "RtlConvertVariantToProperty: 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 = (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 (pvar->vt == (VT_VECTOR | VT_BSTR)) { // Initialize the # bytes to copy. cbcopy = BSTRLEN(pv); // Verify that the BSTR is terminated. if (((OLECHAR const *) pv)[cbcopy/sizeof(OLECHAR)] != OLESTR('\0')) { PROPASSERT(((OLECHAR const *) pv)[cbcopy/sizeof(OLECHAR)] == OLESTR('\0')); StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: 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)); RtlpConvertToMultiByte( (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)); RtlpConvertToUnicode( (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 (pvar->vt == (VT_VECTOR | VT_BSTR)) // Otherwise it's an LPSTR else { PROPASSERT(IsAnsiString((char const *) pv, MAXULONG)); PROPASSERT(pvar->vt == (VT_VECTOR | VT_LPSTR)); cbcopy = strlen((char *) pv) + 1; // + trailing null if (CodePage == CP_WINUNICODE) { PROPASSERT(IsAnsiString( (CHAR const *) pv, cbcopy)); RtlpConvertToUnicode( (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 of Variants? if (cbch == MAXULONG) { // Yes. Convert this variant. if (pprop != NULL) { RtlConvertVariantToPropertyNoEH( (PROPVARIANT *) ppv, CodePage, (SERIALIZEDPROPERTYVALUE *) pbdst, &cbcopy, PID_ILLEGAL, TRUE, NULL, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; pbdst += cbcopy; } ppv = (VOID **) Add2Ptr(ppv, sizeof(PROPVARIANT)); } // if (cbch == MAXULONG) else { // This is a vector of something other than Variants. 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 cbVectElement; // Calculate the length of the vector element. cbVectElement = (ULONG) cbcopy/cbchdiv; // Is this a ClipData? if (cbch == sizeof(CLIPDATA)) { // Adjust the length to include sizeof(ulClipFmt) cbVectElement += sizeof(ULONG); // Write the vector element length. *(ULONG *) pbdst = PropByteSwap( cbVectElement ); // Advance pbdst & write the clipboard format. pbdst += sizeof(ULONG); *(ULONG *) pbdst = PropByteSwap( ((CLIPDATA *) ppv)->ulClipFmt ); } else // This isn't a ClipFormat vector element. { // Write the vector element length. *(ULONG *) pbdst = PropByteSwap( cbVectElement ); } // 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++; delete [] pchConvert; pchConvert = NULL; } } // if (cbch == MAXULONG) ... else } // for (cElems = *pcount; cElems > 0; cElems--) } // if (cbch != 0) // STRING/VARIANT/CF VECTOR property else { // This isn't a vector, or if it is, the elements // 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); } // 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) { // Does this property have a count field? if (pcount != NULL) { // Write the count & advance pbdst *(ULONG *) pbdst = PropByteSwap( *pcount ); pbdst += sizeof(ULONG); } // Is this a VT_CF? if (pclipfmt != NULL) { // Write the ClipFormat & advance pbdst *(ULONG *) pbdst = PropByteSwap( (DWORD) *pclipfmt ); pbdst += sizeof(ULONG); } } // 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, "RtlConvertVariantToProperty: 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 ); } // 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( (DWORD) pvar->vt ); } // Update the caller's copy of the total size. *pcb = DwordAlign(cb); Exit: delete [] pchConvert; return(pprop); } //+--------------------------------------------------------------------------- // Function: RtlConvertPropertyToVariant, 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 //--------------------------------------------------------------------------- #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. BOOLEAN RtlConvertPropertyToVariant( IN SERIALIZEDPROPERTYVALUE const *pprop, IN USHORT CodePage, OUT PROPVARIANT *pvar, IN PMemoryAllocator *pma) { BOOLEAN boolRet; NTSTATUS status; boolRet = RtlConvertPropertyToVariantNoEH( pprop, CodePage, pvar, pma, &status ); if (!NT_SUCCESS( status )) RtlRaiseStatus( status ); return (boolRet); } // Now define the body of the function, returning errors with an // NTSTATUS value instead of raising. BOOLEAN RtlConvertPropertyToVariantNoEH( IN SERIALIZEDPROPERTYVALUE const *pprop, 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; 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 ); const BOOLEAN fConvertToEmpty = FALSE; // --------------------------------------------------------- // Based on the VT, calculate ppv, pv, cbskip, // cb, fPostAllocInit, fNullLegal, & fConvertToEmpty // --------------------------------------------------------- // 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); switch (pvar->vt) { case VT_EMPTY: case VT_NULL: break; #ifdef PROPVAR_VT_I1 case VT_I1: AssertByteField(cVal); // VT_I1 #endif 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_UI4: case VT_R4: case VT_ERROR: AssertLongField(lVal); // VT_I4 AssertLongField(ulVal); // VT_UI4 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_CF: // Allocate a CLIPDATA buffer pvar->pclipdata = (CLIPDATA *) pma->Allocate(sizeof(CLIPDATA)); if (pvar->pclipdata == NULL) { StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory for CF"); goto Exit; } // Set the size (includes sizeof(ulClipFmt)) pvar->pclipdata->cbSize = PropByteSwap( ((CLIPDATA *) pprop->rgb)->cbSize ); if( pvar->pclipdata->cbSize < sizeof(pvar->pclipdata->ulClipFmt) ) { StatusError(pstatus, "RtlConvertPropertyToVariant: 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_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 // [length field] bytes should be allocated cb = PropByteSwap( *(ULONG *) pprop->rgb ); // When a buffer is allocated, it's pointer will go // in *ppv. ppv = (VOID **) &pvar->pszVal; // Is this a non-empty string? if (cb != 0) { // Is the serialized value one that should be // an Ansi string in the PropVariant? if (pvar->vt == VT_LPSTR // It's an LPSTR (always Ansi), or || !OLECHAR_IS_UNICODE ) // PropVariant strings are Ansi { // If the propset is Unicode, we must do a // conversion to Ansi. if (CodePage == CP_WINUNICODE) { WCHAR *pwsz = (WCHAR *) Add2ConstPtr(pprop->rgb, 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. RtlpConvertToMultiByte( 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(pprop->rgb, sizeof(ULONG)), cb)); RtlpConvertToUnicode( (CHAR const *) Add2ConstPtr(pprop->rgb, 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 this is a BSTR property, verify that it is terminated // appropriately. if (VT_BSTR == pvar->vt) { BSTR bstr = ( NULL == pchConvert ) ? (BSTR) Add2ConstPtr(pprop->rgb, sizeof(ULONG)) : (BSTR) pchConvert; // On little-endian machines, validate the string. #ifdef LITTLEENDIAN PROPASSERT( IsOLECHARString( bstr, MAXULONG )); #endif // Validate the bstr. Note that even though this bstr may // be byte-swapped, this 'if' block still works because // ByteSwap('\0') == ('\0'). PROPASSERT( PropByteSwap( (OLECHAR) OLESTR('\0') ) == (OLECHAR) OLESTR('\0') ); if( (cb & (sizeof(OLECHAR) - 1)) != 0 && OLECHAR_IS_UNICODE || bstr[cb/sizeof(OLECHAR) - 1] != OLESTR('\0') ) { StatusError(pstatus, "RtlConvertPropertyToVariant: Invalid BSTR Property", STATUS_INTERNAL_DB_CORRUPTION); goto Exit; } } // if (VT_BSTR == pvar->vt) } // if (cb != 0) fNullLegal = TRUE; break; case VT_LPWSTR: fNullLegal = TRUE; AssertStringField(pwszVal); // VT_LPWSTR cb = PropByteSwap( *(ULONG *) pprop->rgb ) * sizeof(WCHAR); ppv = (VOID **) &pvar->pwszVal; // If byte-swapping will be necessary, do so for the WCHARs CBBYTESWAP( sizeof(WCHAR) ); break; #ifdef PROPVAR_VT_I1 case VT_VECTOR | VT_I1: AssertByteVector(cac); // VT_I1 #endif 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(ULONG); 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; default: DebugTrace(0, DEBTRACE_ERROR, ( "RtlConvertPropertyToVariant: unsupported vt=%x\n", pvar->vt)); StatusInvalidParameter(pstatus, "RtlConvertPropertyToVariant: bad type"); goto Exit; } // switch (pvar->vt) // ------------------------------------------------------ // We've now analyzed the serialized property and learned // about it, now we can put it into the PropVariant. // ------------------------------------------------------ // Is this a simple, unaligned scalar? if (pv != NULL) { // Yes. All we need to do is copy some bytes. PROPASSERT(pchConvert == NULL); RtlCopyMemory(pv, pprop->rgb, cb); // We also might need to byte-swap them (but only in the PropVar). PBSBuffer( pv, cb, cbByteSwap ); } // Otherwise, we need to allocate memory, to which the // PropVariant will point. else if (ppv != NULL) { *ppv = NULL; if (!fConvertToEmpty && cb == 0) // Kernel only { if (!fNullLegal) { StatusInvalidParameter(pstatus, "RtlConvertPropertyToVariant: bad NULL"); goto Exit; } } else { PROPASSERT(cb != 0 || fConvertToEmpty); // 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 with SysStringAlloc. if( VT_BSTR != pvar->vt ) { *ppv = pma->Allocate(max(1, cb)); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory"); goto Exit; } } // Can we load the PropVariant with a simple copy? if (!fPostAllocInit) { // Yes - all we need is a copy (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'. PROPASSERT( NULL == *ppv ); *ppv = (*UnicodeCallouts.pfnSysAllocString)( ( pchConvert != NULL ) ? (OLECHAR *) pchConvert : (OLECHAR *) (pprop->rgb + cbskip) ); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory"); goto Exit; } } 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 copy. // (Thus this is a vector of strings, variants, or CFs). ULONG cElems = pvar->calpstr.cElems; // Initialize the source pointer to point just beyond // the element count. BYTE const *pbsrc = pprop->rgb + sizeof(ULONG); // Zero all pointers in the pElems array for easy caller cleanup ppv = (VOID **) *ppv; RtlZeroMemory(ppv, cb); // Handle Variants, ClipFormats, & Strings separately. if (pvar->vt == (VT_VECTOR | VT_VARIANT)) { PROPVARIANT *pvarT = (PROPVARIANT *) ppv; PROPASSERT(!fIndirect); while (cElems-- > 0) { ULONG cbelement; fIndirect = RtlConvertPropertyToVariantNoEH( (SERIALIZEDPROPERTYVALUE const *) pbsrc, CodePage, pvarT, pma, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; PROPASSERT(!fIndirect); cbelement = PropertyLengthNoEH( (SERIALIZEDPROPERTYVALUE const *) pbsrc, MAXULONG, CPSS_VARIANTVECTOR, pstatus); if( !NT_SUCCESS(*pstatus) ) goto Exit; pbsrc += cbelement; pvarT++; } } // if (pvar->vt == (VT_VECTOR | VT_VARIANT)) else if (pvar->vt == (VT_VECTOR | VT_CF)) { // 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, "RtlConvertPropertyToVariant: 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, "RtlConvertPropertyToVariant: 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 of some kind of string. { // Assume that characters are CHARs ULONG cbch = sizeof(char); if (pvar->vt == (VT_VECTOR | VT_LPWSTR)) { // Characters are WCHARs cbch = sizeof(WCHAR); // If byte-swapping is enabled, LPWSTRs must have // their WCHARs swapped. CBBYTESWAP( sizeof(WCHAR) ); } while (cElems-- > 0) { ULONG cbcopy; cbcopy = cb = PropByteSwap( *((ULONG *) pbsrc) ) * cbch; pbsrc += sizeof(ULONG); pv = (VOID *) pbsrc; PROPASSERT(*ppv == NULL); PROPASSERT(pchConvert == NULL); if (fConvertToEmpty || cb != 0) { // Do we have actual data to work with? if (cb != 0) { // Special BSTR pre-processing ... if (pvar->vt == (VT_VECTOR | VT_BSTR)) { // 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)); RtlpConvertToUnicode( (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. RtlpConvertToMultiByte( (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 // Verify that the BSTR is valid. if( (cbcopy & (sizeof(OLECHAR)-1)) != 0 && OLECHAR_IS_UNICODE || ((OLECHAR const *) pv)[cbcopy/sizeof(OLECHAR) - 1] != OLESTR('\0') ) { StatusError(pstatus, "RtlConvertPropertyToVariant: Invalid BSTR element", STATUS_INTERNAL_DB_CORRUPTION); goto Exit; } } // if (pvar->vt == (VT_VECTOR | VT_BSTR)) // Special LPSTR pre-processing 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. RtlpConvertToMultiByte( (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)) } // if (cb != 0) // Allocate memory in the PropVariant and copy // the string. if( (VT_BSTR | VT_VECTOR) == pvar->vt ) { // For BSTRs, the allocate/copy is performed // by SysStringAlloc. *ppv = (*UnicodeCallouts.pfnSysAllocString)( (BSTR) pv ); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: 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 { // Allocate a buffer in the PropVariant *ppv = pma->Allocate(max(1, cbcopy)); if (*ppv == NULL) { StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory for string element"); goto Exit; } // 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 delete[] pchByteSwap; pchByteSwap = NULL; delete [] pchConvert; pchConvert = NULL; } // if (fConvertToEmpty || 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 (!fConvertToEmpty && cb == 0) ... else } // else if (ppv != NULL) Exit: delete[] pchByteSwap; delete [] 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 //--------------------------------------------------------------------------- #ifndef KERNEL VOID CleanupVariants( IN PROPVARIANT *pvar, IN ULONG cprop, IN PMemoryAllocator *pma) { while (cprop-- > 0) { VOID *pv = NULL; VOID **ppv = NULL; #ifdef KERNEL ULONG cbbstr = 0; #endif ULONG cElems; switch (pvar->vt) { case VT_CF: pv = pvar->pclipdata; if (pv != NULL && pvar->pclipdata->pClipData) { pma->Free(pvar->pclipdata->pClipData); } break; case VT_BLOB: case VT_BLOB_OBJECT: pv = pvar->blob.pBlobData; break; case VT_BSTR: #ifdef KERNEL cbbstr = sizeof(ULONG); //FALLTHROUGH #endif 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: #ifdef PROPVAR_VT_I1 case VT_VECTOR | VT_I1: AssertByteVector(cac); // VT_I1 #endif 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(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: #ifdef KERNEL cbbstr = sizeof(ULONG); //FALLTHROUGH #endif 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) { #ifdef KERNEL pma->Free((BYTE *) *ppv - cbbstr); #else if( (VT_BSTR | VT_VECTOR) == pvar->vt ) { (*UnicodeCallouts.pfnSysFreeString)( (BSTR) *ppv ); } else { pma->Free((BYTE *) *ppv); } #endif } ppv++; } // Free the vector of pointers. pma->Free(pv); pv = NULL; } // if (ppv != NULL) if (pv != NULL) { #ifdef KERNEL pma->Free((BYTE *) pv - cbbstr); #else if( VT_BSTR == pvar->vt ) { (*UnicodeCallouts.pfnSysFreeString)( (BSTR) pv ); } else { pma->Free((BYTE *) pv); } #endif } pvar->vt = VT_EMPTY; // Move on to the next PropVar in the vector. pvar++; } // while (cprop-- > 0) } #endif // !KERNEL //+-------------------------------------------------------------------------- // 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. 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 ); } // 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 cbremain = cbbuf; ULONG cb = 0, cbch; BOOLEAN fIllegalType = FALSE; *pstatus = STATUS_SUCCESS; if (cbremain < CB_SERIALIZEDPROPERTYVALUE) { StatusOverflow(pstatus, "PropertyLength: dwType"); goto Exit; } cbremain -= CB_SERIALIZEDPROPERTYVALUE; if( PropByteSwap(pprop->dwType) & VT_VECTOR ) { if (cbremain < sizeof(ULONG)) { StatusOverflow(pstatus, "PropertyLength: cElems"); goto Exit; } cbremain -= sizeof(ULONG); cElems = PropByteSwap( *pl++ ); } if( PropByteSwap(pprop->dwType) == (VT_VECTOR | VT_VARIANT) ) { 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; } } else { cbch = sizeof(WCHAR); switch( PropByteSwap(pprop->dwType) & VT_TYPEMASK) { case VT_EMPTY: case VT_NULL: fIllegalType = (flags & CPSS_VARIANTVECTOR) != 0; break; #ifdef PROPVAR_VT_I1 case VT_I1: #endif 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_UI4: 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_BLOB: case VT_BLOB_OBJECT: // FALLTHROUGH case VT_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT: if (flags & CPSS_VARIANTVECTOR) { fIllegalType = TRUE; break; } // FALLTHROUGH case VT_CF: case VT_BSTR: case VT_LPSTR: cbch = sizeof(BYTE); // FALLTHROUGH case VT_LPWSTR: 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; } } if (fIllegalType) { StatusInvalidParameter(pstatus, "PropertyLength: Illegal VarType"); goto Exit; } cb = (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); } //+-------------------------------------------------------------------------- // Function: PropertyLengthAsVariant // // 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 //--------------------------------------------------------------------------- #ifdef WINNT // First, define a wrapper which raises NT Exceptions for compatibility // with older callers who expect it. ULONG PropertyLengthAsVariant( IN SERIALIZEDPROPERTYVALUE const *pprop, IN ULONG cbprop, IN USHORT CodePage, IN BYTE flags) { NTSTATUS status; ULONG ulRet; ulRet = PropertyLengthAsVariantNoEH( 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 PropertyLengthAsVariantNoEH( IN SERIALIZEDPROPERTYVALUE const *pprop, IN ULONG cbprop, IN USHORT CodePage, IN BYTE flags, OUT NTSTATUS *pstatus) { ULONG cElems; ULONG cbvar = 0; *pstatus = STATUS_SUCCESS; PROPASSERT(cbprop == PropertyLengthNoEH(pprop, cbprop, flags, pstatus)); if( PropByteSwap(pprop->dwType) & VT_VECTOR ) { cElems = *(ULONG *) pprop->rgb; } switch (pprop->dwType) { //default: //case VT_EMPTY: //case VT_NULL: //case VT_I1: //case VT_UI1: //case VT_I2: //case VT_UI2: //case VT_BOOL: //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: //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_STREAM: case VT_STREAMED_OBJECT: case VT_STORAGE: case VT_STORED_OBJECT: cbvar = cbprop - 2 * sizeof(ULONG); 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; // Vector properties: #ifdef PROPVAR_VT_I1 case VT_VECTOR | VT_I1: AssertByteVector(cac); // VT_I1 #endif 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(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 AssertStringVector(cabstr); // VT_BSTR // don't include VARTYPE and count fields cbvar = cbprop - 2 * 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 Quad align every string and 1 ULONG to Quad // align the array of string pointers. cbvar += cElems * sizeof(ULONG) + sizeof(ULONG); break; case VT_VECTOR | VT_VARIANT: { ULONG const *pl = (ULONG const *) pprop->rgb + 1; ULONG cbremain = cbprop - 2 * sizeof(ULONG); 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 = PropertyLengthAsVariantNoEH( (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)); } #endif //+-------------------------------------------------------------------------- // 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 = new BYTE[ 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 = new 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 DEFINE_CBufferAllocator__Allocate