/* * propstg.c - Property storage ADT */ #include "priv.h" #include "propstg.h" #ifndef UNIX // SafeGetItemObject // // Since the GetItemObject member of IShellView was added late in the game // during Win95 development we have found at least one example (rnaui.dll) // of an application that built an IShellView with a NULL member for // GetItemObject. Fearing more applications that may have the same // problem, this wrapper function was added to catch bad apps like rnaui. // Thus, we check here for NULL before calling the member. // STDAPI SafeGetItemObject(LPSHELLVIEW psv, UINT uItem, REFIID riid, LPVOID *ppv) { #ifdef __cplusplus #error THIS_MUST_STAY_C // read the comment above #endif if (!psv->lpVtbl->GetItemObject) return E_FAIL; return (HRESULT)(psv->lpVtbl->GetItemObject(psv, uItem, riid, ppv)); } #endif // This structure is a dictionary element. It maps a name to a propid. typedef struct { PROPID propid; WCHAR wszName[MAX_PATH]; } DICTEL, * PDICTEL; // This structure is a propvariant element. typedef struct { PROPVARIANT propvar; DWORD dwFlags; // PEF_* } PROPEL, * PPROPEL; // Flags for PROPEL structure #define PEF_VALID 0x00000001 #define PEF_DIRTY 0x00000002 // This structure is the ADT for property storage typedef struct { DWORD cbSize; DWORD dwFlags; HDSA hdsaProps; // array of properties (indexed by propid) HDPA hdpaDict; // dictionary of names mapped to propid // (each element is a DICTEL) int idsaLastValid; } PROPSTG, * PPROPSTG; // The first two entries in hdsaProps are reserved. When we enumerate // thru the list, we skip these entries. #define PROPID_DICT 0 #define PROPID_CODEPAGE 1 #define IDSA_START 2 #define CDSA_RESERVED 2 BOOL IsValidHPROPSTG(HPROPSTG hstg) { PPROPSTG pstg = (PPROPSTG)hstg; return (IS_VALID_WRITE_PTR(pstg, PROPSTG) && SIZEOF(*pstg) == pstg->cbSize && NULL != pstg->hdsaProps && NULL != pstg->hdpaDict); } #ifdef DEBUG BOOL IsValidPPROPSPEC(PROPSPEC * ppropspec) { return (ppropspec && PRSPEC_PROPID == ppropspec->ulKind || (PRSPEC_LPWSTR == ppropspec->ulKind && IS_VALID_STRING_PTRW(ppropspec->DUMMYUNION_MEMBER(lpwstr), -1))); } #endif STDAPI PropStg_Create(OUT HPROPSTG * phstg, IN DWORD dwFlags) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_WRITE_PTR(phstg, HPROPSTG))) { PPROPSTG pstg = (PPROPSTG)LocalAlloc(LPTR, SIZEOF(*pstg)); hres = STG_E_INSUFFICIENTMEMORY; // assume error if (pstg) { pstg->cbSize = SIZEOF(*pstg); pstg->dwFlags = dwFlags; pstg->idsaLastValid = PROPID_CODEPAGE; pstg->hdsaProps = DSA_Create(SIZEOF(PROPEL), 8); pstg->hdpaDict = DPA_Create(8); if (pstg->hdsaProps && pstg->hdpaDict) { // The first two propids are reserved, so insert // placeholders. PROPEL propel; propel.propvar.vt = VT_EMPTY; propel.dwFlags = 0; DSA_SetItem(pstg->hdsaProps, PROPID_DICT, &propel); DSA_SetItem(pstg->hdsaProps, PROPID_CODEPAGE, &propel); hres = S_OK; } else { // Clean up because something failed if (pstg->hdsaProps) { DSA_Destroy(pstg->hdsaProps); pstg->hdsaProps = NULL; } if (pstg->hdpaDict) { DPA_Destroy(pstg->hdpaDict); pstg->hdpaDict = NULL; } LocalFree(pstg); pstg = NULL; } } *phstg = (HPROPSTG)pstg; // Validate return values ASSERT((SUCCEEDED(hres) && IS_VALID_WRITE_PTR(*phstg, PPROPSTG)) || (FAILED(hres) && NULL == *phstg)); } return hres; } STDAPI PropStg_Destroy(HPROPSTG hstg) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG))) { PPROPSTG pstg = (PPROPSTG)hstg; if (pstg->hdsaProps) { int cdsa = DSA_GetItemCount(pstg->hdsaProps) - CDSA_RESERVED; // The first two elements are not cleared, because they // are just place-holders. if (0 < cdsa) { PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START); ASSERT(ppropel); while (0 < cdsa--) { PropVariantClear(&ppropel->propvar); ppropel++; } } DSA_Destroy(pstg->hdsaProps); pstg->hdsaProps = NULL; } if (pstg->hdpaDict) { int i, cel = DPA_GetPtrCount(pstg->hdpaDict); for (i = 0; i < cel; i++) { LocalFree(DPA_FastGetPtr(pstg->hdpaDict, i)); } DPA_Destroy(pstg->hdpaDict); pstg->hdpaDict = NULL; } LocalFree(pstg); pstg = NULL; hres = S_OK; } return hres; } /*---------------------------------------------------------- Purpose: Compare names Returns: standard -1, 0, 1 Cond: -- */ int CALLBACK PropStg_Compare(IN LPVOID pv1, IN LPVOID pv2, IN LPARAM lParam) { LPCWSTR psz1 = pv1; LPCWSTR psz2 = pv2; // Case insensitive return StrCmpW(psz1, psz2); } /*---------------------------------------------------------- Purpose: Returns TRUE if the property exists in this storage. If it does exist, the propid is returned. Returns: TRUE FALSE Cond: -- */ BOOL PropStg_PropertyExists(IN PPROPSTG pstg, IN const PROPSPEC * ppropspec, OUT PROPID * ppropid) { BOOL bRet; PPROPEL ppropel; HDSA hdsaProps; ASSERT(pstg); ASSERT(ppropspec); ASSERT(ppropid); hdsaProps = pstg->hdsaProps; switch (ppropspec->ulKind) { case PRSPEC_PROPID: *ppropid = ppropspec->DUMMYUNION_MEMBER(propid); bRet = (*ppropid < (PROPID)DSA_GetItemCount(hdsaProps)); if (bRet) { ppropel = DSA_GetItemPtr(hdsaProps, *ppropid); bRet = (ppropel && IsFlagSet(ppropel->dwFlags, PEF_VALID)); } break; case PRSPEC_LPWSTR: // Key off whether the name exists *ppropid = DPA_Search(pstg->hdpaDict, ppropspec->DUMMYUNION_MEMBER(lpwstr), 0, PropStg_Compare, 0, DPAS_SORTED); #ifdef DEBUG // Sanity check that the property actually exists ppropel = DSA_GetItemPtr(hdsaProps, *ppropid); ASSERT(-1 == *ppropid || (ppropel && IsFlagSet(ppropel->dwFlags, PEF_VALID))); #endif bRet = (-1 != *ppropid); break; default: bRet = FALSE; break; } // Propid values 0 and 1 are reserved, as are values >= 0x80000000 if (bRet && (0 == *ppropid || 1 == *ppropid || 0x80000000 <= *ppropid)) bRet = FALSE; return bRet; } /*---------------------------------------------------------- Purpose: Create a new propid and assign the given name to it. The propid is an index into hdsaProps. Returns: S_OK STG_E_INSUFFICIENTMEMORY Cond: -- */ HRESULT PropStg_NewPropid(IN PPROPSTG pstg, IN LPCWSTR pwsz, IN PROPID propidFirst, OUT PROPID * ppropid) OPTIONAL { HRESULT hres = STG_E_INVALIDPOINTER; // assume error DICTEL * pdictel; PROPID propid = (PROPID)-1; HDPA hdpa; ASSERT(pstg); ASSERT(ppropid); if (EVAL(IS_VALID_STRING_PTRW(pwsz, -1))) { hres = STG_E_INSUFFICIENTMEMORY; // assume error hdpa = pstg->hdpaDict; // The name shouldn't be in the list yet ASSERT(-1 == DPA_Search(hdpa, (LPVOID)pwsz, 0, PropStg_Compare, 0, DPAS_SORTED)); pdictel = LocalAlloc(LPTR, SIZEOF(*pdictel)); if (pdictel) { // Determine the propid for this PROPID propidNew = max(propidFirst, (PROPID)pstg->idsaLastValid + 1); pdictel->propid = propidNew; StrCpyNW(pdictel->wszName, pwsz, ARRAYSIZE(pdictel->wszName)); if (-1 != DPA_AppendPtr(hdpa, pdictel)) { // Sort it by name DPA_Sort(hdpa, PropStg_Compare, 0); hres = S_OK; propid = propidNew; } } } *ppropid = propid; return hres; } /*---------------------------------------------------------- Purpose: Read a set of properties given their propids. If the propid doesn't exist in this property storage, then set the value type to VT_EMPTY but return success; unless all the properties in rgpropspec don't exist, in which case also return S_FALSE. Returns: S_OK S_FALSE STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY Cond: -- */ STDAPI PropStg_ReadMultiple(IN HPROPSTG hstg, IN ULONG cpspec, IN const PROPSPEC * rgpropspec, IN PROPVARIANT * rgpropvar) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) && EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec)) && EVAL(IS_VALID_READ_BUFFER(rgpropvar, PROPVARIANT, cpspec))) { PPROPSTG pstg = (PPROPSTG)hstg; ULONG cpspecSav = cpspec; const PROPSPEC * ppropspec = rgpropspec; PROPVARIANT * ppropvar = rgpropvar; int idsa; BOOL bPropertyExists; ULONG cpspecIllegal = 0; hres = S_OK; // assume success if (0 < cpspec) { // Read the list of property specs while (0 < cpspec--) { bPropertyExists = PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa); // Does this property exist? if ( !bPropertyExists ) { // No ppropvar->vt = VT_ILLEGAL; cpspecIllegal++; } else { // Yes; is the element a valid property? PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, idsa); ASSERT(ppropel); if (IsFlagSet(ppropel->dwFlags, PEF_VALID)) { // Yes; copy the variant value hres = PropVariantCopy(ppropvar, &ppropel->propvar); } else { // No ppropvar->vt = VT_ILLEGAL; cpspecIllegal++; } } ppropspec++; ppropvar++; // Bail out of loop if something failed if (FAILED(hres)) break; } // Are all the property specs illegal? if (cpspecIllegal == cpspecSav) { hres = S_FALSE; // yes } // Did anything fail above? if (FAILED(hres)) { // Yes; clean up -- no properties will be retrieved FreePropVariantArray(cpspecSav, rgpropvar); } } } return hres; } /*---------------------------------------------------------- Purpose: Add a set of property values given their propids. If the propid doesn't exist in this property storage, then add the propid as a legal ID and set the value. On error, some properties may or may not have been written. If pfn is non-NULL, this callback will get called to optionally "massage" the propvariant value or to validate it. The rules for the callback are: 1) It can change the value directly if it is not allocated 2) If the value is allocated, the callback must replace the pointer with a newly allocated buffer that it allocates. It must not try to free the value coming in, since it doesn't know how it was allocated. It must also use CoTaskMemAlloc to allocate its buffer. 3) If the callback returns an error, this function will stop writing properties and return that error. 4) If the callback returns S_FALSE, this function will not write that particular property and continue on to the next property. The function then returns S_FALSE once it is finished. Returns: S_OK S_FALSE STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY Cond: -- */ STDAPI PropStg_WriteMultipleEx(IN HPROPSTG hstg, IN ULONG cpspec, IN const PROPSPEC * rgpropspec, IN const PROPVARIANT * rgpropvar, IN PROPID propidFirst, OPTIONAL IN PFNPROPVARMASSAGE pfn, OPTIONAL IN LPARAM lParam) OPTIONAL { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) && EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec)) && EVAL(IS_VALID_READ_BUFFER(rgpropvar, PROPVARIANT, cpspec))) { PPROPSTG pstg = (PPROPSTG)hstg; const PROPSPEC * ppropspec = rgpropspec; const PROPVARIANT * ppropvar = rgpropvar; int idsa; PROPEL propel; BOOL bPropertyExists; BOOL bSkippedProperty = FALSE; if (0 == cpspec) { hres = S_OK; } else { // Write the list of property specs while (0 < cpspec--) { bPropertyExists = PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa); hres = S_OK; // If this is an illegal variant type and yet a valid // property, then return an error. Otherwise, ignore it // and move on. if (VT_ILLEGAL == ppropvar->vt) { if (bPropertyExists) hres = STG_E_INVALIDPARAMETER; else goto NextDude; } if (SUCCEEDED(hres)) { // Add the property. If it doesn't exist, add it. // Is this a propid or a name? switch (ppropspec->ulKind) { case PRSPEC_PROPID: idsa = ppropspec->DUMMYUNION_MEMBER(propid); break; case PRSPEC_LPWSTR: if ( !bPropertyExists ) { hres = PropStg_NewPropid(pstg, ppropspec->DUMMYUNION_MEMBER(lpwstr), propidFirst, (LPDWORD)&idsa); } break; default: hres = STG_E_INVALIDNAME; break; } if (SUCCEEDED(hres)) { PROPVARIANT propvarT; ASSERT(S_OK == hres); // we're assuming this on entry // Save a copy of the original in case the // callback changes it. CopyMemory(&propvarT, ppropvar, SIZEOF(propvarT)); // How did the callback like it? if (pfn) hres = pfn(idsa, ppropvar, lParam); if (S_OK == hres) { // Fine; make a copy of the (possibly changed) // propvariant value hres = PropVariantCopy(&propel.propvar, ppropvar); if (SUCCEEDED(hres)) { propel.dwFlags = PEF_VALID | PEF_DIRTY; hres = (DSA_SetItem(pstg->hdsaProps, idsa, &propel) ? S_OK : STG_E_INSUFFICIENTMEMORY); if (SUCCEEDED(hres) && idsa > pstg->idsaLastValid) { pstg->idsaLastValid = idsa; } } } else if (S_FALSE == hres) { bSkippedProperty = TRUE; } // Restore the propvariant value to its original // value. But first, did the callback allocate a // new buffer? if (propvarT.DUMMYUNION_MEMBER(pszVal) != ppropvar->DUMMYUNION_MEMBER(pszVal)) { // Yes; clear it (this function is safe for // non-allocated values too). PropVariantClear((PROPVARIANT *)ppropvar); } // Restore CopyMemory((PROPVARIANT *)ppropvar, &propvarT, SIZEOF(*ppropvar)); hres = S_OK; } } NextDude: ppropspec++; ppropvar++; // Bail out of loop if something failed if (FAILED(hres)) break; } if (bSkippedProperty) hres = S_FALSE; } } return hres; } /* Purpose: Add a set of property values given their propids. If the propid doesn't exist in this property storage, then add the propid as a legal ID and set the value. On error, some properties may or may not have been written. Returns: S_OK STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY */ STDAPI PropStg_WriteMultiple(IN HPROPSTG hstg, IN ULONG cpspec, IN const PROPSPEC * rgpropspec, IN const PROPVARIANT * rgpropvar, IN PROPID propidFirst) OPTIONAL { return PropStg_WriteMultipleEx(hstg, cpspec, rgpropspec, rgpropvar, propidFirst, NULL, 0); } STDAPI PropStg_DeleteMultiple(IN HPROPSTG hstg, IN ULONG cpspec, IN const PROPSPEC * rgpropspec) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) && EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec))) { PPROPSTG pstg = (PPROPSTG)hstg; const PROPSPEC * ppropspec = rgpropspec; HDSA hdsaProps = pstg->hdsaProps; PPROPEL ppropel; int idsa; int cdsa; hres = S_OK; if (0 < cpspec) { BOOL bDeletedLastValid = FALSE; // Delete the list of property specs while (0 < cpspec--) { if (PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa)) { // Delete the property. Zero out the existing // propel. Don't call DSA_DeleteItem, otherwise // we'll move the positions of any remaining // properties following this one, thus changing their // propids. ppropel = DSA_GetItemPtr(hdsaProps, idsa); ASSERT(ppropel); PropVariantClear(&ppropel->propvar); ppropel->dwFlags = 0; // Our idsaLastValid is messed up if we hit this // assert ASSERT(idsa <= pstg->idsaLastValid); if (idsa == pstg->idsaLastValid) bDeletedLastValid = TRUE; // Delete the names associated with the property // FEATURE (scotth): implement this } ppropspec++; } // Did we delete the property that was marked as the terminating // valid property in the list? if (bDeletedLastValid) { // Yes; go back and search for the new terminating index ppropel = DSA_GetItemPtr(hdsaProps, pstg->idsaLastValid); cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED; ASSERT(ppropel); while (0 < cdsa--) { if (IsFlagSet(ppropel->dwFlags, PEF_VALID)) { pstg->idsaLastValid = cdsa - 1; break; } ppropel--; } if (0 == cdsa) pstg->idsaLastValid = PROPID_CODEPAGE; } // Since we didn't delete any items from hdsaProps (we freed // the variant value and zeroed it out), this structure // may have a bunch of unused elements at the end. // Compact now if necessary. // Do we have a bunch of trailing, empty elements? cdsa = DSA_GetItemCount(hdsaProps); if (cdsa > pstg->idsaLastValid + 1) { // Yes; compact. Start from the end and go backwards // so DSA_DeleteItem doesn't have to move memory blocks. for (idsa = cdsa-1; idsa > pstg->idsaLastValid; idsa--) { #ifdef DEBUG ppropel = DSA_GetItemPtr(hdsaProps, idsa); ASSERT(IsFlagClear(ppropel->dwFlags, PEF_VALID)); #endif DSA_DeleteItem(hdsaProps, idsa); } } } } return hres; } /*---------------------------------------------------------- Purpose: Marks the specified properties dirty or undirty, depending on the value of bDirty. Returns: S_OK STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY Cond: -- */ STDAPI PropStg_DirtyMultiple(IN HPROPSTG hstg, IN ULONG cpspec, IN const PROPSPEC * rgpropspec, IN BOOL bDirty) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) && EVAL(IS_VALID_READ_BUFFER(rgpropspec, PROPSPEC, cpspec))) { PPROPSTG pstg = (PPROPSTG)hstg; const PROPSPEC * ppropspec = rgpropspec; HDSA hdsaProps = pstg->hdsaProps; PPROPEL ppropel; int idsa; hres = S_OK; if (0 < cpspec) { // Mark the list of property specs while (0 < cpspec--) { // Does it exist? if (PropStg_PropertyExists(pstg, ppropspec, (LPDWORD)&idsa)) { // Yes; mark it ppropel = DSA_GetItemPtr(hdsaProps, idsa); ASSERT(ppropel); if (bDirty) { SetFlag(ppropel->dwFlags, PEF_DIRTY); } else { ClearFlag(ppropel->dwFlags, PEF_DIRTY); } } ppropspec++; } } } return hres; } /*---------------------------------------------------------- Purpose: Marks or unmarks all the property values. Returns: S_OK STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY Cond: -- */ STDAPI PropStg_DirtyAll(IN HPROPSTG hstg, IN BOOL bDirty) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG))) { PPROPSTG pstg = (PPROPSTG)hstg; int cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED; hres = S_OK; if (0 < cdsa) { PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START); ASSERT(ppropel); while (0 < cdsa--) { if (bDirty) SetFlag(ppropel->dwFlags, PEF_DIRTY); else ClearFlag(ppropel->dwFlags, PEF_DIRTY); ppropel++; } } } return hres; } /*---------------------------------------------------------- Purpose: Returns S_OK if at least one property value is dirty in the storage. Otherwise, this function returns S_FALSE. Returns: S_OK if it is dirty S_FALSE if not STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY Cond: -- */ STDAPI PropStg_IsDirty(HPROPSTG hstg) { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG))) { PPROPSTG pstg = (PPROPSTG)hstg; int cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED; hres = S_FALSE; if (0 < cdsa) { PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START); ASSERT(ppropel); while (0 < cdsa--) { if (IsFlagSet(ppropel->dwFlags, PEF_DIRTY)) { hres = S_OK; break; } ppropel++; } } } return hres; } /*---------------------------------------------------------- Purpose: Enumerates thru the list of properties. Returns: S_OK STG_E_INVALIDPARAMETER STG_E_INSUFFICIENTMEMORY Cond: -- */ STDAPI PropStg_Enum(IN HPROPSTG hstg, IN DWORD dwFlags, // One of PSTGEF_ IN PFNPROPSTGENUM pfnEnum, IN LPARAM lParam) OPTIONAL { HRESULT hres = STG_E_INVALIDPARAMETER; if (EVAL(IS_VALID_HANDLE(hstg, PROPSTG)) && EVAL(IS_VALID_CODE_PTR(pfnEnum, PFNPROPSTGENUM))) { PPROPSTG pstg = (PPROPSTG)hstg; int cdsa = pstg->idsaLastValid + 1 - CDSA_RESERVED; DWORD dwFlagsPEF = 0; hres = S_OK; // Set the filter flags if (dwFlags & PSTGEF_DIRTY) SetFlag(dwFlagsPEF, PEF_DIRTY); if (0 < cdsa) { PPROPEL ppropel = DSA_GetItemPtr(pstg->hdsaProps, IDSA_START); int idsa = IDSA_START; ASSERT(ppropel); while (0 < cdsa--) { // Does it pass thru filter? if (IsFlagSet(ppropel->dwFlags, PEF_VALID) && (0 == dwFlagsPEF || (dwFlagsPEF & ppropel->dwFlags))) { // Yes, call callback HRESULT hresT = pfnEnum(idsa, &ppropel->propvar, lParam); if (S_OK != hresT) { if (FAILED(hresT)) hres = hresT; break; // stop enumeration } } ppropel++; idsa++; } } } return hres; } #ifdef DEBUG HRESULT CALLBACK PropStg_DumpVar(IN PROPID propid, IN PROPVARIANT * ppropvar, IN LPARAM lParam) { TCHAR sz[MAX_PATH]; PPROPEL ppropel = (PPROPEL)ppropvar; // we're cheating here if (IsFlagSet(ppropel->dwFlags, PEF_DIRTY)) wnsprintf(sz, ARRAYSIZE(sz), TEXT(" *id:%#lx\t%s"), propid, Dbg_GetVTName(ppropvar->vt)); else wnsprintf(sz, ARRAYSIZE(sz), TEXT(" id:%#lx\t%s"), propid, Dbg_GetVTName(ppropvar->vt)); switch (ppropvar->vt) { case VT_EMPTY: case VT_NULL: case VT_ILLEGAL: TraceMsg(TF_ALWAYS, " %s", sz); break; case VT_I2: case VT_I4: TraceMsg(TF_ALWAYS, " %s\t%d", sz, ppropvar->DUMMYUNION_MEMBER(lVal)); break; case VT_UI1: TraceMsg(TF_ALWAYS, " %s\t%#02x '%c'", sz, ppropvar->DUMMYUNION_MEMBER(bVal), ppropvar->DUMMYUNION_MEMBER(bVal)); break; case VT_UI2: TraceMsg(TF_ALWAYS, " %s\t%#04x", sz, ppropvar->DUMMYUNION_MEMBER(uiVal)); break; case VT_UI4: TraceMsg(TF_ALWAYS, " %s\t%#08x", sz, ppropvar->DUMMYUNION_MEMBER(ulVal)); break; case VT_LPSTR: TraceMsg(TF_ALWAYS, " %s\t\"%S\"", sz, Dbg_SafeStrA(ppropvar->DUMMYUNION_MEMBER(pszVal))); break; case VT_LPWSTR: TraceMsg(TF_ALWAYS, " %s\t\"%ls\"", sz, Dbg_SafeStrW(ppropvar->DUMMYUNION_MEMBER(pwszVal))); break; default: #if defined(_WIN64) TraceMsg(TF_ALWAYS, " %s\t0x%p", sz, (DWORD_PTR)ppropvar->DUMMYUNION_MEMBER(pszVal)); #else TraceMsg(TF_ALWAYS, " s\t%#08lx", sz, (DWORD)ppropvar->DUMMYUNION_MEMBER(pszVal)); #endif break; } return S_OK; } STDAPI PropStg_Dump(IN HPROPSTG hstg, IN DWORD dwFlags) // One of PSTGDF_ { TraceMsg(TF_ALWAYS, " Property storage 0x%08lx = {", hstg); PropStg_Enum(hstg, 0, PropStg_DumpVar, 0); TraceMsg(TF_ALWAYS, " }"); return NOERROR; } #endif