3639 lines
96 KiB
C++
3639 lines
96 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1993.
|
|
//
|
|
// File: propstg.cxx
|
|
//
|
|
// Contents: Class that directly implements IPropertyStorage
|
|
//
|
|
// Classes: CCoTaskAllocator
|
|
// CPropertyStorage
|
|
//
|
|
// Notes: For methods that state 'if successful returns S_OK,
|
|
// otherwise error code', the possible error codes include:
|
|
//
|
|
// STG_E_INVALIDHANDLE
|
|
// STG_E_INSUFFICIENTMEMORY
|
|
// STG_E_MEDIUMFULL
|
|
// STG_E_REVERTED
|
|
// STG_E_INVALIDPARAMETER
|
|
// STG_E_INVALIDFLAG
|
|
//
|
|
// History: 1-Mar-95 BillMo Created.
|
|
// 22-Feb-96 MikeHill Use VT_EMPTY instead of VT_ILLEGAL.
|
|
// 14-Mar-96 MikeHill Set _fUserDefinedProperties in open constructor.
|
|
// 09-May-96 MikeHill Don't return an error when someone calls
|
|
// IPropertyStorage::Revert on a direct-mode propset.
|
|
// 22-May-96 MikeHill Use the new _dwOSVersion.
|
|
// 06-Jun-96 MikeHill Validate inputs.
|
|
// 31-Jul-96 MikeHill - Treat prop names as OLECHARs, not WCHARs
|
|
// - Added CDocFilePropertyStorage
|
|
// - Modified for Mac support.
|
|
// 07-Feb-97 Danl - Removed CDocFilePropertyStorage.
|
|
// 10-Mar-98 MikeHill - Only stat for the grfMode on create/open
|
|
// if it wasn't provided by the caller.
|
|
// - Dbg outputs.
|
|
// 5/6/98 MikeHill
|
|
// - Use CoTaskMem rather than new/delete.
|
|
// - Removed calls for defunct UnicodeCallouts.
|
|
// - Added dbgouts.
|
|
// - Changed GetCPropertySetStream to GetFormatVersion (DBG only).
|
|
// 5/18/98 MikeHill
|
|
// - Fixed typos.
|
|
// 6/11/98 MikeHill
|
|
// - Allow the codepage to change during WriteMultiple.
|
|
// 8/18/98 MikeHill
|
|
// - If the given _grfMode is zero, then probe the stream
|
|
// to see if it's actually writeable.
|
|
// - InitializePropertyStream now determines the CREATEPROP_
|
|
// flags internally.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#include <tstr.h>
|
|
|
|
#ifdef _MAC_NODOC
|
|
ASSERTDATA // File-specific data for FnAssert
|
|
#endif
|
|
|
|
#ifndef _MAC // No InfoLevel debug functionality on Mac.
|
|
DECLARE_INFOLEVEL(prop)
|
|
#endif
|
|
|
|
|
|
extern "C" const IID
|
|
IID_IStorageTest = { /* 40621cf8-a17f-11d1-b28d-00c04fb9386d */
|
|
0x40621cf8,
|
|
0xa17f,
|
|
0x11d1,
|
|
{0xb2, 0x8d, 0x00, 0xc0, 0x4f, 0xb9, 0x38, 0x6d}
|
|
};
|
|
|
|
// The IMappedStream is implemented by all the IStream implementations,
|
|
// and provides a mapping for CPropertySetStream.
|
|
|
|
extern "C" const IID
|
|
IID_IMappedStream = { /* 7d747d7f-a49e-11d1-b28e-00c04fb9386d */
|
|
0x7d747d7f,
|
|
0xa49e,
|
|
0x11d1,
|
|
{0xb2, 0x8e, 0x00, 0xc0, 0x4f, 0xb9, 0x38, 0x6d}
|
|
};
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CCoTaskAllocator::Allocate, Free.
|
|
//
|
|
// Synopsis: A PMemoryAllocator used by the Pr*
|
|
// property set routines. This is required
|
|
// so that those routines can work in any
|
|
// heap.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
void *
|
|
CCoTaskAllocator::Allocate(ULONG cbSize)
|
|
{
|
|
return( CoTaskMemAlloc(cbSize) );
|
|
}
|
|
|
|
void
|
|
CCoTaskAllocator::Free(void *pv)
|
|
{
|
|
CoTaskMemFree( pv );
|
|
}
|
|
|
|
|
|
const OLECHAR g_oszPropertyContentsStreamName[] = OLESTR( "CONTENTS" );
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Initialize
|
|
//
|
|
// Synopsis: Initialize members to known values.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
void CPropertyStorage::Initialize()
|
|
{
|
|
_fExplicitelyProbedForWriteAccess = FALSE;
|
|
_fUserDefinedProperties = FALSE;
|
|
_ulSig = PROPERTYSTORAGE_SIG;
|
|
_cRefs = 1;
|
|
_pstgPropSet = NULL;
|
|
_pstmPropSet = NULL;
|
|
_dwOSVersion = PROPSETHDR_OSVERSION_UNKNOWN;
|
|
_np = NULL;
|
|
_ms = NULL;
|
|
_usCodePage = CP_WINUNICODE;
|
|
_grfFlags = 0;
|
|
_grfMode = 0;
|
|
|
|
#if DBG
|
|
_cLocks = 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::InitializePropertyStream.
|
|
//
|
|
// Synopsis: Initialize the storage-type specific members.
|
|
//
|
|
// Arguments: [pguid] -- FMTID, in for create only.
|
|
// [pclsid] -- Class id, in for create only.
|
|
// [CreateOpenDelete] -- has one of the following
|
|
// values: CREATE_PROPSTREAM, OPEN_PROPSTREAM, or
|
|
// DELETE_PROPSTREAM.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Requires:
|
|
// _pstmPropSet -- The IStream of the main property set stream.
|
|
//
|
|
// Modifies:
|
|
// _ms (IMappedStream *)
|
|
//
|
|
// (assumed NULL on entry) will be NULL or valid on exit
|
|
//
|
|
// _np (NTPROP) aka CPropertySetStream
|
|
//
|
|
// (assumed NULL on entry) will be NULL or valid on exit
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::InitializePropertyStream(
|
|
const GUID *pguid,
|
|
GUID const *pclsid,
|
|
EInitializePropertyStream CreateOpenDelete )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD grfBehavior = 0;
|
|
USHORT createprop = 0; // Flags parameter to PrCreatePropertySet
|
|
|
|
propITrace( "CPropertyStorage::InitializePropertyStream" );
|
|
AssertLocked();
|
|
|
|
|
|
// Set the CREATEPROP_ flags in createprop
|
|
|
|
if( CREATE_PROPSTREAM == CreateOpenDelete )
|
|
createprop = CREATEPROP_CREATE;
|
|
else if( DELETE_PROPSTREAM == CreateOpenDelete )
|
|
createprop = CREATEPROP_DELETE;
|
|
else
|
|
{
|
|
DfpAssert( OPEN_PROPSTREAM == CreateOpenDelete );
|
|
|
|
// If _grfMode is zero, it's either uninitialized or STGM_READ|STGM_SHARE_DENYNONE.
|
|
// We'll consider it unknown for now, and probe the stream later to see if it's
|
|
// writeable.
|
|
|
|
if( 0 == _grfMode )
|
|
createprop = CREATEPROP_UNKNOWN;
|
|
else
|
|
createprop = IsWriteable() ? CREATEPROP_WRITE : CREATEPROP_READ;
|
|
}
|
|
|
|
if( IsNonSimple() )
|
|
createprop |= CREATEPROP_NONSIMPLE;
|
|
|
|
|
|
// In the create path, set the behavior flag that will be passed to
|
|
// PrCreatePropertySet. In the open path, this will be returned
|
|
// from that function instead.
|
|
|
|
if( PROPSETFLAG_CASE_SENSITIVE & _grfFlags
|
|
&&
|
|
CREATEPROP_CREATE & createprop )
|
|
{
|
|
grfBehavior = PROPSET_BEHAVIOR_CASE_SENSITIVE;
|
|
}
|
|
|
|
|
|
// Get an appropriate IMappedStream
|
|
hr = CreateMappedStream();
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Create the CPropertySetStream
|
|
|
|
NTSTATUS Status;
|
|
|
|
DfpAssert( NULL == _np );
|
|
|
|
Status = PrCreatePropertySet(
|
|
(NTMAPPEDSTREAM)_ms,
|
|
createprop,
|
|
pguid,
|
|
pclsid,
|
|
(NTMEMORYALLOCATOR) &_cCoTaskAllocator,
|
|
GetUserDefaultLCID(),
|
|
&_dwOSVersion,
|
|
&_usCodePage,
|
|
&grfBehavior,
|
|
&_np);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
propDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::InitializePropertyStream"
|
|
" - PrCreatePropertySet Status=%08X\n", this, Status));
|
|
hr = DfpNtStatusToHResult(Status);
|
|
goto Exit;
|
|
}
|
|
|
|
// If this was a create, the input _grfFlags should match the Behavior
|
|
DfpAssert( (PROPSETFLAG_CASE_SENSITIVE & _grfFlags) && (PROPSET_BEHAVIOR_CASE_SENSITIVE & grfBehavior)
|
|
||
|
|
!(PROPSETFLAG_CASE_SENSITIVE & _grfFlags) && !(PROPSET_BEHAVIOR_CASE_SENSITIVE & grfBehavior)
|
|
||
|
|
!(CREATEPROP_CREATE & createprop) );
|
|
|
|
// Also if this was a create, the input _grfFlags should match the codepage
|
|
DfpAssert( (PROPSETFLAG_ANSI & _grfFlags) && CP_WINUNICODE != _usCodePage
|
|
||
|
|
!(PROPSETFLAG_ANSI & _grfFlags) && CP_WINUNICODE == _usCodePage
|
|
||
|
|
!(CREATEPROP_CREATE & createprop) );
|
|
|
|
// If this is an open, we need to update _grfFlags with the actual values,
|
|
// so that we can return them in a Stat.
|
|
|
|
if( CP_WINUNICODE != _usCodePage )
|
|
_grfFlags |= PROPSETFLAG_ANSI;
|
|
|
|
if( PROPSET_BEHAVIOR_CASE_SENSITIVE & grfBehavior )
|
|
_grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
|
|
|
|
Exit:
|
|
|
|
if( STG_E_FILENOTFOUND == hr )
|
|
propSuppressExitErrors();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::~CPropertyStorage
|
|
//
|
|
// Synopsis: Free up object resources.
|
|
//
|
|
// Notes: Cleans up even from partial construction.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CPropertyStorage::~CPropertyStorage()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
propITrace( "CPropertyStorage::~CPropertyStorage" );
|
|
|
|
Lock();
|
|
|
|
_ulSig = PROPERTYSTORAGE_SIGDEL; // prevent someone else deleting it
|
|
|
|
// Close the property set. This causes the latest mapped stream data to be
|
|
// written to the underlying stream. Errors are ignored, though, so
|
|
// clients should call Commit before calling the final release, in order
|
|
// to get an opportunity to recover from flush errors.
|
|
|
|
if (_np != NULL)
|
|
{
|
|
PrClosePropertySet(_np);
|
|
}
|
|
|
|
// Free the mapped stream.
|
|
|
|
DeleteMappedStream();
|
|
|
|
// Free the Stream and/or Storage with the serialized data.
|
|
// If it was opened in direct mode, then the data written
|
|
// during PrClosePropertySet will be implicitely commited.
|
|
// If it was opened in transacted mode, then data written
|
|
// in PrClosePropertySet will be reverted and lost (of course,
|
|
// to avoid this, the client would have called IPropertyStorage::Commit).
|
|
|
|
RELEASE_INTERFACE( _pstmPropSet );
|
|
|
|
if( _pstgPropSet != NULL )
|
|
{
|
|
// If we're not opened in transacted mode, call Commit.
|
|
// This was added to handle NFF (NTFS property sets), in which case
|
|
// we open a direct IStorage, but it actually gives us a transacted
|
|
// storage, for the purpose of robustness.
|
|
// We tell IsWriteable not to probe the stream if it's unsure
|
|
// about the _grfMode; return FALSE in that case.
|
|
|
|
if( IsWriteable(DO_NOT_PROBE) && !(STGM_TRANSACTED & _grfMode) )
|
|
_pstgPropSet->Commit( STGC_DEFAULT );
|
|
|
|
RELEASE_INTERFACE( _pstgPropSet );
|
|
}
|
|
|
|
if (_fInitCriticalSection)
|
|
DeleteCriticalSection( &_CriticalSection );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::QueryInterface, AddRef, Release
|
|
//
|
|
// Synopsis: IUnknown members
|
|
//
|
|
// Notes: IPropertyStorage supports IPropertyStorage and IUnknown
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
HRESULT CPropertyStorage::QueryInterface( REFIID riid, void **ppvObject)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = ValidateRef()))
|
|
return(hr);
|
|
|
|
// Validate the inputs
|
|
|
|
VDATEREADPTRIN( &riid, IID );
|
|
VDATEPTROUT( ppvObject, void* );
|
|
|
|
// -----------------
|
|
// Perform the Query
|
|
// -----------------
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if( IID_IPropertyStorage == riid || IID_IUnknown == riid )
|
|
{
|
|
*ppvObject = static_cast<IPropertyStorage*>(this);
|
|
CPropertyStorage::AddRef();
|
|
}
|
|
|
|
#if DBG
|
|
else if( IID_IStorageTest == riid )
|
|
{
|
|
*ppvObject = static_cast<IStorageTest*>(this);
|
|
CPropertyStorage::AddRef();
|
|
}
|
|
#endif // #if DBG
|
|
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
ULONG CPropertyStorage::AddRef(void)
|
|
{
|
|
if (S_OK != ValidateRef())
|
|
return(0);
|
|
|
|
InterlockedIncrement(&_cRefs);
|
|
return(_cRefs);
|
|
}
|
|
|
|
ULONG CPropertyStorage::Release(void)
|
|
{
|
|
LONG lRet;
|
|
|
|
if (S_OK != ValidateRef())
|
|
return(0);
|
|
|
|
lRet = InterlockedDecrement(&_cRefs);
|
|
|
|
if (lRet == 0)
|
|
{
|
|
delete this; // this will do a flush if dirty
|
|
}
|
|
else
|
|
if (lRet <0)
|
|
{
|
|
lRet = 0;
|
|
}
|
|
return(lRet);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::CleanupOpenedObjects
|
|
//
|
|
// Synopsis: Cleans up the objects that have been opened
|
|
// during the ReadMultiple. Sets all entries to
|
|
// VT_ILLEGAL so that the later free doesn't try to
|
|
// treat the pointers as interface pointers.
|
|
//
|
|
// Arguments: [avar] -- The user's array of PROPVARIANTs
|
|
//
|
|
// [pip] -- The array of INDIRECTPROPERTY structures
|
|
// for non-simple properties.
|
|
//
|
|
// [cpspec] -- if 1 then no MAX_ULONG end of list marker.
|
|
//
|
|
// [iFailIndex] -- An index into [pip] which
|
|
// indicates the non-simple property
|
|
// which failed to open, and represents
|
|
// the index at which the avar's begin
|
|
// to be strings rather than IStream's et al.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID CPropertyStorage::CleanupOpenedObjects(
|
|
PROPVARIANT avar[],
|
|
INDIRECTPROPERTY *pip,
|
|
ULONG cpspec,
|
|
ULONG iFailIndex)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG iStgProp;
|
|
ULONG iiScan;
|
|
|
|
propITrace( "CPropertyStorage::CleanupOpenedObjects" );
|
|
AssertLocked();
|
|
|
|
// the one that fails is passed in as ppPropVarFail.
|
|
|
|
for (iiScan = 0;
|
|
(iStgProp = pip[iiScan].Index) != MAX_ULONG;
|
|
iiScan++)
|
|
{
|
|
// since we've just opened a bunch of storages we should
|
|
// release them in this error case. We don't release the
|
|
// one at ppPropVarFail because that one is still a string.
|
|
|
|
PROPVARIANT *pPropVar = avar + iStgProp;
|
|
|
|
if (iiScan < iFailIndex)
|
|
{
|
|
switch (pPropVar->vt)
|
|
{
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
pPropVar->pStream->Release();
|
|
break;
|
|
case VT_STORAGE:
|
|
case VT_STORED_OBJECT:
|
|
pPropVar->pStorage->Release();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree( pPropVar->pStream );
|
|
}
|
|
|
|
pPropVar->vt = VT_ILLEGAL;
|
|
pPropVar->pStream = NULL; // mark pStorage and pStream as nul
|
|
|
|
if (cpspec == 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::ReadMultiple
|
|
//
|
|
// Synopsis: Read properties from the property set.
|
|
//
|
|
// Arguments: [cpspec] -- Count of PROPSPECs in [rgpspec]
|
|
// [rgpspec] -- Array of PROPSPECs
|
|
// [rgpropvar] -- Array of PROPVARIANTs to be filled in
|
|
// with callee allocated data.
|
|
//
|
|
// Returns: S_FALSE if none found
|
|
// S_OK if >=1 found
|
|
// FAILED(hr) otherwise.
|
|
//
|
|
// Notes: SPEC: Returning the same IStream* for the same
|
|
// VT queried multiple times.
|
|
//
|
|
// PrQueryProperties has been specified to return
|
|
// useful data: the count of properties found (controls
|
|
// return code) and an array of indexes of non-simple
|
|
// PROPSPECs (useful for simply opening the storages and
|
|
// streams.) This extra returned data means we don't
|
|
// have to walk the [rgpropvar] in the success cases.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::ReadMultiple(
|
|
ULONG cpspec,
|
|
const PROPSPEC rgpspec[],
|
|
PROPVARIANT rgpropvar[])
|
|
{
|
|
NTSTATUS Status;
|
|
HRESULT hr;
|
|
INDIRECTPROPERTY * pip; //array for non-simple
|
|
INDIRECTPROPERTY ip;
|
|
ULONG cpropFound;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::ReadMultiple" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate inputs
|
|
|
|
if (0 == cpspec)
|
|
{
|
|
hr = S_FALSE;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPSPEC( cpspec, rgpspec )))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = ValidateOutRGPROPVARIANT( cpspec, rgpropvar )))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "cpspec=%d, rgpspec=%08X, rgpropvar=%08X",
|
|
cpspec, rgpspec, rgpropvar ));
|
|
|
|
// -------------------
|
|
// Read the Properties
|
|
// -------------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsReadable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
Status = PrQueryProperties(
|
|
_np,
|
|
cpspec,
|
|
rgpspec,
|
|
NULL, // don't want PROPID's
|
|
cpspec == 1 ? (INDIRECTPROPERTY**)&ip : &pip,
|
|
rgpropvar,
|
|
&cpropFound);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (cpropFound != 0)
|
|
{
|
|
if (cpspec == 1)
|
|
{
|
|
if (ip.Index != MAX_ULONG)
|
|
{
|
|
pip = &ip;
|
|
}
|
|
else
|
|
{
|
|
pip = NULL;
|
|
}
|
|
}
|
|
|
|
if (pip != NULL)
|
|
{
|
|
|
|
// we have one or more of VT_STREAM, VT_STREAMED_OBJECT,
|
|
// VT_STORAGE, VT_STORED_OBJECT, VT_VERSIONED_STREAM
|
|
|
|
ULONG iiScan;
|
|
ULONG iStgProp;
|
|
|
|
for (iiScan = 0;
|
|
hr == S_OK && (iStgProp = pip[iiScan].Index) != MAX_ULONG;
|
|
iiScan++ )
|
|
{
|
|
PROPVARIANT *pPropVar = rgpropvar + iStgProp;
|
|
OLECHAR **pposzStreamOrStorageName = NULL;
|
|
|
|
if (IsNonSimple() && pPropVar->pwszVal[0] != L'\0')
|
|
{
|
|
VOID *pStreamOrStorage = NULL;
|
|
|
|
switch (pPropVar->vt)
|
|
{
|
|
case VT_VERSIONED_STREAM:
|
|
|
|
pposzStreamOrStorageName
|
|
= reinterpret_cast<OLECHAR**>( &(pPropVar->pVersionedStream->pStream) );
|
|
// Fall through
|
|
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
|
|
if( NULL == pposzStreamOrStorageName )
|
|
{
|
|
pposzStreamOrStorageName
|
|
= reinterpret_cast<OLECHAR**>( &(pPropVar->pStream) );
|
|
}
|
|
|
|
// Mask out the STGM_TRANSACTED bit because we don't
|
|
// support it.
|
|
|
|
hr = _pstgPropSet->OpenStream(*pposzStreamOrStorageName,
|
|
NULL,
|
|
GetChildOpenMode() & ~STGM_TRANSACTED,
|
|
0,
|
|
(IStream**)&pStreamOrStorage);
|
|
break;
|
|
|
|
case VT_STORAGE:
|
|
case VT_STORED_OBJECT:
|
|
|
|
if( NULL == pposzStreamOrStorageName )
|
|
{
|
|
pposzStreamOrStorageName
|
|
= reinterpret_cast<OLECHAR**>( &(pPropVar->pStorage) );
|
|
}
|
|
|
|
hr = _pstgPropSet->OpenStorage(*pposzStreamOrStorageName,
|
|
NULL,
|
|
GetChildOpenMode(),
|
|
NULL,
|
|
0,
|
|
(IStorage**)&pStreamOrStorage);
|
|
break;
|
|
|
|
default:
|
|
|
|
DfpAssert( !OLESTR("Invalid non-simple property type") );
|
|
hr = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR );
|
|
|
|
} // switch (pPropVar->vt)
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// The pStream/pStorage entry currently points to a string
|
|
// (the name of the stream/storage). Delete that string buffer
|
|
// and put in the real stream/storage interface pointer.
|
|
|
|
CoTaskMemFree( *pposzStreamOrStorageName );
|
|
*pposzStreamOrStorageName = reinterpret_cast<OLECHAR*>( pStreamOrStorage );
|
|
|
|
}
|
|
else if (hr != STG_E_FILENOTFOUND)
|
|
{
|
|
// the one that fails is passed in as
|
|
// iiScan and is still a string.
|
|
CleanupOpenedObjects(rgpropvar, pip, cpspec, iiScan);
|
|
}
|
|
} // if (IsNonSimple() && pPropVar->pwszVal[0] != L'\0')
|
|
else
|
|
{
|
|
hr = STG_E_FILENOTFOUND;
|
|
}
|
|
|
|
if (hr == STG_E_FILENOTFOUND)
|
|
{
|
|
// if the stream/storage is not found, or this is
|
|
// a simple stream with VT_STORAGE etc, then treat
|
|
// like the property is not found.
|
|
|
|
if( VT_VERSIONED_STREAM == pPropVar->vt )
|
|
{
|
|
pPropVar->pVersionedStream->pStream->Release();
|
|
CoTaskMemFree( pPropVar->pVersionedStream );
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree( pPropVar->pszVal );
|
|
}
|
|
|
|
PropVariantInit( pPropVar );
|
|
--cpropFound;
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (cpspec == 1)
|
|
break;
|
|
}
|
|
|
|
if (cpspec != 1 && pip != NULL)
|
|
CoTaskMemFree( pip );
|
|
|
|
} // if (pip != NULL)
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
// we succeeded in getting the basic property types but
|
|
// the non-simple stuff failed, so we zap out the whole lot
|
|
// and return a complete failure
|
|
FreePropVariantArray(cpspec, rgpropvar);
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK && cpropFound == 0)
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
if( HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) == hr )
|
|
propSuppressExitErrors();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::_WriteMultiple, private
|
|
//
|
|
// Synopsis: Write the properties to the property set. Allows
|
|
// a NULL rgpropvar pointer for deletion case.
|
|
//
|
|
// Arguments: [cpspec] -- count of PROPSPECs and PROPVARIANTs in
|
|
// [rgpspec] and [rgpropvar]
|
|
//
|
|
// [rgpspec] -- pointer to array of PROPSPECs
|
|
//
|
|
// [rgpropvar] -- pointer to array of PROPVARIANTs with
|
|
// the values to write.
|
|
//
|
|
// [propidNameFirst] -- id below which not to assign
|
|
// ids for named properties.
|
|
//
|
|
//
|
|
// Returns: S_OK, -- all requested data was written.
|
|
// S_FALSE -- all simple properties written, but non-simple
|
|
// types (VT_STREAM etc) were ignored.
|
|
// Errors --
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Notes: PrSetProperties has been carefully specified to return
|
|
// useful information so that we can deal with the case
|
|
// where a non-simple type (VT_STREAM etc) is overwritten
|
|
// by a simple type.
|
|
//
|
|
// This routine assumes the object has been validated
|
|
// and is writeable.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::_WriteMultiple(
|
|
ULONG cpspec,
|
|
const PROPSPEC rgpspec[],
|
|
const PROPVARIANT rgpropvar[],
|
|
PROPID propidNameFirst)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
CStackPropIdArray rgPROPID;
|
|
|
|
INDIRECTPROPERTY * pip;
|
|
INDIRECTPROPERTY ip;
|
|
|
|
if (S_OK != (hr = rgPROPID.Init(cpspec)))
|
|
return(hr);
|
|
|
|
propITrace( "CPropertyStorage::_WriteMultiple" );
|
|
AssertLocked();
|
|
|
|
Status = PrSetProperties(_np, // property set context
|
|
cpspec, // property count
|
|
propidNameFirst, // first propid for new named props
|
|
rgpspec, // array of property specifiers
|
|
&_usCodePage, // updated CodePage
|
|
rgPROPID, // buffer for array of propids
|
|
// the stream/storage names for indirect properties
|
|
1 == cpspec ? (INDIRECTPROPERTY**)&ip : &pip,
|
|
rgpropvar);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
// The code page may have been modified. Update grfFlags to
|
|
// reflect the current value.
|
|
|
|
if( CP_WINUNICODE == _usCodePage )
|
|
_grfFlags &= ~PROPSETFLAG_ANSI;
|
|
else
|
|
_grfFlags |= PROPSETFLAG_ANSI;
|
|
|
|
// Point 'pip' to the INDIRECTPROPERTY array
|
|
|
|
if (cpspec == 1)
|
|
{
|
|
if (ip.Index != MAX_ULONG)
|
|
pip = &ip;
|
|
else
|
|
pip = NULL;
|
|
}
|
|
|
|
// If we have indirect properties, write them out now.
|
|
|
|
if ( pip != NULL)
|
|
{
|
|
ULONG iiScan; // in this scope because we always use
|
|
ULONG iStgProp; // these variables in the free memory loop below.
|
|
|
|
if (IsSimple())
|
|
{
|
|
//
|
|
// VT_STREAM was requested to be written and this
|
|
// is a "SIMPLE" property set.
|
|
//
|
|
hr = STG_E_PROPSETMISMATCHED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Two cases now:
|
|
// 1. Wrote a simple over a non-simple -- must delete the
|
|
// old non-simple.
|
|
// 2. Wrote a non-simple -- must actually copy data into it.
|
|
//
|
|
|
|
|
|
for (iiScan = 0;
|
|
hr == S_OK &&
|
|
(iStgProp = pip[iiScan].Index) != MAX_ULONG;
|
|
iiScan++ )
|
|
{
|
|
|
|
OLECHAR oszStdPropName[sizeof("prop")+10+1];
|
|
const OLECHAR *poszPropName;
|
|
const PROPVARIANT *pPropVar = rgpropvar + iStgProp;
|
|
IStream *pstmFrom = NULL;
|
|
|
|
poszPropName = static_cast<LPOLESTR>(pip[iiScan].poszName);
|
|
|
|
if( NULL == poszPropName )
|
|
{
|
|
DfpAssert((LONG) iStgProp >= 0 && iStgProp < cpspec);
|
|
PROPGENPROPERTYNAME( oszStdPropName, rgPROPID[iStgProp] );
|
|
poszPropName = oszStdPropName;
|
|
}
|
|
|
|
DfpAssert( NULL != poszPropName );
|
|
|
|
switch (rgpropvar == NULL ? VT_ILLEGAL : pPropVar->vt)
|
|
{
|
|
|
|
case VT_VERSIONED_STREAM:
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
{
|
|
IStream *pstm;
|
|
int i=0;
|
|
|
|
if( VT_VERSIONED_STREAM == pPropVar->vt )
|
|
pstmFrom = pPropVar->pVersionedStream->pStream;
|
|
else
|
|
pstmFrom = pPropVar->pStream;
|
|
|
|
while (i<=1)
|
|
{
|
|
hr = _pstgPropSet->CreateStream(poszPropName,
|
|
GetChildCreateMode() & ~STGM_TRANSACTED,
|
|
0, 0, &pstm);
|
|
if (hr == S_OK)
|
|
{
|
|
if( NULL != pstmFrom )
|
|
{
|
|
ULARGE_INTEGER uli;
|
|
memset(&uli, -1, sizeof(uli));
|
|
hr = pstmFrom->CopyTo(pstm, uli, NULL, NULL);
|
|
}
|
|
pstm->Release();
|
|
break;
|
|
}
|
|
else
|
|
if (hr != STG_E_FILEALREADYEXISTS)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
if (i == 0)
|
|
{
|
|
_pstgPropSet->DestroyElement(poszPropName);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
break;
|
|
case VT_STORAGE:
|
|
case VT_STORED_OBJECT:
|
|
{
|
|
IStorage *pstg;
|
|
int i=0;
|
|
while (i<=1)
|
|
{
|
|
hr = _pstgPropSet->CreateStorage(poszPropName,
|
|
GetChildCreateMode(),
|
|
0,
|
|
0,
|
|
&pstg);
|
|
if (hr == S_OK)
|
|
{
|
|
if (pPropVar->pStorage != NULL)
|
|
{
|
|
hr = pPropVar->pStorage->CopyTo(0, NULL,
|
|
NULL, pstg);
|
|
}
|
|
pstg->Release();
|
|
break;
|
|
}
|
|
else
|
|
if (hr != STG_E_FILEALREADYEXISTS)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
if (i == 0)
|
|
{
|
|
_pstgPropSet->DestroyElement(poszPropName);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
//
|
|
// Any other VT_ type is simple and therefore
|
|
// was a non-simple overwritten by a simple.
|
|
//
|
|
hr = _pstgPropSet->DestroyElement( poszPropName );
|
|
break;
|
|
}
|
|
|
|
if (cpspec == 1)
|
|
break;
|
|
|
|
} // for (iiScan = 0; ...
|
|
} // if (IsSimple())
|
|
|
|
// In both the success and failure cases we do this cleanup.
|
|
|
|
for (iiScan = 0; pip[iiScan].Index != MAX_ULONG; iiScan++ )
|
|
{
|
|
if (pip[iiScan].poszName != NULL)
|
|
CoTaskMemFree( pip[iiScan].poszName );
|
|
|
|
if (cpspec == 1)
|
|
break;
|
|
}
|
|
|
|
if (cpspec != 1 && pip != NULL)
|
|
CoTaskMemFree( pip );
|
|
|
|
} // if ( pip != NULL)
|
|
else
|
|
{
|
|
//
|
|
// No VT_STREAM etc was requested to be written.
|
|
// and no simple property overwrote a non-simple one.
|
|
}
|
|
} // if (NT_SUCCESS(Status))
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
if( HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) == hr )
|
|
propSuppressExitErrors();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::WriteMultiple
|
|
//
|
|
// Synopsis: Write properties.
|
|
//
|
|
// Arguments: [cpspec] -- count of PROPSPECs and PROPVARIANTs in
|
|
// [rgpspec] and [rgpropvar]
|
|
// [rgpspec] -- pointer to array of PROPSPECs
|
|
// [rgpropvar] -- pointer to array of PROPVARIANTs with
|
|
// the values to write.
|
|
// [propidNameFirst] -- id below which not to assign
|
|
// ids for named properties.
|
|
//
|
|
// Returns: S_OK, -- all requested data was written.
|
|
// S_FALSE -- all simple properties written, but non-simple
|
|
// types (VT_STREAM etc) were ignored.
|
|
// Errors --
|
|
//
|
|
// Notes: Checks that rgpropvar is not NULL, then calls
|
|
// _WriteMultiple.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::WriteMultiple(
|
|
ULONG cpspec,
|
|
const PROPSPEC rgpspec[],
|
|
const PROPVARIANT rgpropvar[],
|
|
PROPID propidNameFirst)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::WriteMultiple" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if (0 == cpspec)
|
|
{
|
|
hr = S_OK;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPSPEC( cpspec, rgpspec )))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = ValidateInRGPROPVARIANT( cpspec, rgpropvar )))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "cpspec=%d, rgpspec=%08X, rgpropvar=%08X, propidNameFirst=%d",
|
|
cpspec, rgpspec, rgpropvar, propidNameFirst ));
|
|
|
|
// Ensure we understand all the VarTypes in the input array.
|
|
hr = ValidateVTs( cpspec, rgpropvar );
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// --------------------
|
|
// Write the Properties
|
|
// --------------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
hr = _WriteMultiple(cpspec, rgpspec, rgpropvar, propidNameFirst);
|
|
if (hr == STG_E_INSUFFICIENTMEMORY)
|
|
{
|
|
hr = S_OK;
|
|
|
|
for (ULONG i=0; hr == S_OK && i < cpspec; i++)
|
|
{
|
|
hr = _WriteMultiple(1, rgpspec+i, rgpropvar+i, propidNameFirst);
|
|
if( FAILED(hr) ) goto errRet;
|
|
}
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
if( HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) == hr )
|
|
propSuppressExitErrors();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::DeleteMultiple
|
|
//
|
|
// Synopsis: Delete properties.
|
|
//
|
|
// Arguments: [cpspec] -- count of PROPSPECs and PROPVARIANTs in
|
|
// [rgpspec] and [rgpropvar]
|
|
// [rgpspec] -- pointer to array of PROPSPECs
|
|
//
|
|
// Returns: S_OK, -- all requested data was deleted.
|
|
// S_FALSE -- all simple properties written, but non-simple
|
|
// types (VT_STREAM etc) were ignored.
|
|
// Errors --
|
|
//
|
|
// Notes: Checks that rgpropvar is not NULL, then calls
|
|
// _WriteMultiple.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::DeleteMultiple(
|
|
ULONG cpspec,
|
|
const PROPSPEC rgpspec[])
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::DeleteMultiple" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if (0 == cpspec)
|
|
{
|
|
hr = S_OK;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPSPEC( cpspec, rgpspec )))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "cpspec=%d, rgpspec=%08X", cpspec, rgpspec ));
|
|
|
|
// ---------------------
|
|
// Delete the Properties
|
|
// ---------------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
hr = _WriteMultiple(cpspec, rgpspec, NULL, 2);
|
|
if (hr == STG_E_INSUFFICIENTMEMORY)
|
|
{
|
|
hr = S_OK;
|
|
|
|
for (ULONG i=0; hr == S_OK && i < cpspec; i++)
|
|
{
|
|
hr = _WriteMultiple(1, rgpspec+i, NULL, 2);
|
|
if( FAILED(hr) ) goto errRet;
|
|
}
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::ReadPropertyNames
|
|
//
|
|
// Synopsis: Attempt to read names for all identified properties.
|
|
//
|
|
// Arguments: [cpropid] -- Count of PROPIDs in [rgpropid]
|
|
// [rgpropid] -- Pointer to array of [cpropid] PROPIDs
|
|
// [rglpstrName] -- Pointer to array of [cpropid] LPOLESTRs
|
|
//
|
|
// Returns: S_OK -- success, one or more names returned
|
|
// S_FALSE -- success, no names returned
|
|
// STG_E_INVALIDHEADER -- no propid->name mapping property
|
|
// other errors -- STG_E_INSUFFICIENTMEMORY etc
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::ReadPropertyNames(
|
|
ULONG cpropid,
|
|
const PROPID rgpropid[],
|
|
LPOLESTR rglpwstrName[])
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::ReadPropertyNames" );
|
|
|
|
// --------
|
|
// Validate
|
|
// --------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if (0 == cpropid)
|
|
{
|
|
hr = S_FALSE;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPID( cpropid, rgpropid )))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = ValidateOutRGLPOLESTR( cpropid, rglpwstrName )))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "cpropid=%d, rgpropid=%08X, rglpwstrName=%08X",
|
|
cpropid, rgpropid, rglpwstrName ));
|
|
|
|
// --------------
|
|
// Read the Names
|
|
// --------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsReadable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
Status = PrQueryPropertyNames(_np, cpropid, rgpropid, rglpwstrName);
|
|
if (Status == STATUS_NOT_FOUND)
|
|
hr = STG_E_INVALIDHEADER;
|
|
else
|
|
if (Status == STATUS_BUFFER_ALL_ZEROS)
|
|
hr = S_FALSE;
|
|
else
|
|
if (!NT_SUCCESS(Status))
|
|
hr = DfpNtStatusToHResult(Status);
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::_WritePropertyNames
|
|
//
|
|
// Synopsis: Internal function used by WritePropertyNames and
|
|
// DeletePropertyNames.
|
|
//
|
|
// Arguments: [cpropid] -- Count of PROPIDs in [rgpropid]
|
|
// [rgpropid] -- Pointer to array of [cpropid] PROPIDs
|
|
// [rglpstrName] -- Pointer to array of [cpropid] LPOLESTRs
|
|
//
|
|
// Returns: S_OK if successful, otherwise error code.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::_WritePropertyNames(
|
|
ULONG cpropid,
|
|
const PROPID rgpropid[],
|
|
const LPOLESTR rglpwstrName[])
|
|
{
|
|
NTSTATUS Status;
|
|
AssertLocked();
|
|
|
|
Status = PrSetPropertyNames(_np, cpropid, rgpropid, (OLECHAR const * const *) rglpwstrName);
|
|
return NT_SUCCESS(Status) ? S_OK : DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::WritePropertyNames
|
|
//
|
|
// Synopsis: Attempt to write names for all identified properties.
|
|
//
|
|
// Arguments: [cpropid] -- Count of PROPIDs in [rgpropid]
|
|
// [rgpropid] -- Pointer to array of [cpropid] PROPIDs
|
|
// [rglpstrName] -- Pointer to array of [cpropid] LPOLESTRs
|
|
//
|
|
// Returns: S_OK -- success, otherwise error code.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::WritePropertyNames(
|
|
ULONG cpropid,
|
|
const PROPID rgpropid[],
|
|
const LPOLESTR rglpwstrName[])
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::WritePropertyNames" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate inputs
|
|
|
|
if (0 == cpropid)
|
|
{
|
|
hr = S_OK;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPID( cpropid, rgpropid )))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = ValidateInRGLPOLESTR( cpropid, rglpwstrName )))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "cpropid=%d, rgpropid=%08X, rglpwstrName=%08X",
|
|
cpropid, rgpropid, rglpwstrName ));
|
|
|
|
// ---------------
|
|
// Write the Names
|
|
// ---------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
hr = _WritePropertyNames(cpropid, rgpropid, rglpwstrName);
|
|
|
|
if (hr == STG_E_INSUFFICIENTMEMORY)
|
|
{
|
|
hr = S_OK;
|
|
|
|
for (ULONG i=0; hr == S_OK && i < cpropid; i++)
|
|
{
|
|
hr = _WritePropertyNames(1, rgpropid+i, rglpwstrName+i);
|
|
if( FAILED(hr) ) goto errRet;
|
|
}
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::DeletePropertyNames
|
|
//
|
|
// Synopsis: Attempt to delete names for all identified properties.
|
|
//
|
|
// Arguments: [cpropid] -- Count of PROPIDs in [rgpropid]
|
|
// [rgpropid] -- Pointer to array of [cpropid] PROPIDs
|
|
//
|
|
// Returns: S_OK -- success, otherwise error.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::DeletePropertyNames(
|
|
ULONG cpropid,
|
|
const PROPID rgpropid[])
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::DeletePropertyNames" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if( 0 == cpropid )
|
|
{
|
|
hr = S_OK;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPID( cpropid, rgpropid )))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "cpropid=%d, rgpropid=%08X)", cpropid, rgpropid ));
|
|
|
|
// ----------------
|
|
// Delete the Names
|
|
// ----------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
hr = _WritePropertyNames(cpropid, rgpropid, NULL);
|
|
if (hr == STG_E_INSUFFICIENTMEMORY)
|
|
{
|
|
hr = S_OK;
|
|
|
|
for (ULONG i=0; hr == S_OK && i < cpropid; i++)
|
|
{
|
|
hr = _WritePropertyNames(1, rgpropid+i, NULL);
|
|
if( FAILED(hr) ) goto errRet;
|
|
}
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Commit
|
|
//
|
|
// Synopsis: Flush and/or commit the property set
|
|
//
|
|
// Arguments: [grfCommittFlags] -- Commit flags.
|
|
//
|
|
// Returns: S_OK -- success, otherwise error.
|
|
//
|
|
// Notes: For both simple and non-simple, this flushes the
|
|
// memory image to disk subsystem. In addition,
|
|
// for non-simple transacted-mode property sets, this
|
|
// performs a commit on the property set.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::Commit(DWORD grfCommitFlags)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::Commit" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if (S_OK != (hr = VerifyCommitFlags(grfCommitFlags)))
|
|
goto errRet;
|
|
|
|
propTraceParameters(( "grfCommitFlags=%08X", grfCommitFlags ));
|
|
|
|
// --------------------------
|
|
// Commit the PropertyStorage
|
|
// --------------------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
if (hr == S_OK)
|
|
hr = _pstgPropSet->Commit(grfCommitFlags);
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Revert
|
|
//
|
|
// Synopsis: For non-simple property sets, revert it.
|
|
//
|
|
// Returns: S_OK if successful. STG_E_UNIMPLEMENTEDFUNCTION for
|
|
// simple property sets.
|
|
//
|
|
// Notes: For non-simple property sets, call the underlying
|
|
// storage's Revert and re-open the 'contents' stream.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::Revert()
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::Revert" );
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
Lock(); fLocked = TRUE;
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
hr = _pstgPropSet->Revert();
|
|
if (hr == S_OK)
|
|
{
|
|
PrClosePropertySet(_np);
|
|
_np = NULL;
|
|
|
|
_pstmPropSet->Release();
|
|
_pstmPropSet = NULL;
|
|
|
|
DeleteMappedStream();
|
|
|
|
// if one of these fails then this object becomes invalid (zombie)
|
|
// Mask out the STGM_TRANSACTED bit because we don't support it.
|
|
|
|
hr = _pstgPropSet->OpenStream(g_oszPropertyContentsStreamName, NULL,
|
|
GetChildOpenMode() & ~STGM_TRANSACTED,
|
|
0, &_pstmPropSet);
|
|
if (hr == S_OK)
|
|
{
|
|
// Initialize the property set. If this property set is the 2nd section
|
|
// of the DocumentSummaryInformation property set (used by Office),
|
|
// then we must specify the FMTID.
|
|
|
|
hr = InitializePropertyStream(
|
|
_fUserDefinedProperties ? &FMTID_UserDefinedProperties : NULL,
|
|
NULL, // pguid
|
|
OPEN_PROPSTREAM );
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
_ulSig = PROPERTYSTORAGE_SIGZOMBIE;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
hr = S_OK;
|
|
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Enum
|
|
//
|
|
// Synopsis: Create an enumerator over the property set.
|
|
//
|
|
// Arguments: [ppenum] -- where to return the IEnumSTATPROPSTG *
|
|
//
|
|
// Returns: S_OK or error.
|
|
//
|
|
// Notes: The constructor of CEnumSTATPROPSTG creates a
|
|
// CStatArray which reads the entire property set and
|
|
// which can be shared when IEnumSTATPROPSTG::Clone is
|
|
// used.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::Enum(IEnumSTATPROPSTG ** ppenum)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
IStatArray *psa = NULL;
|
|
IEnumSTATPROPSTG *penum = NULL;
|
|
|
|
propXTrace( "CPropertyStorage::Enum" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
// Validate the inputs
|
|
|
|
VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
|
|
*ppenum = NULL;
|
|
|
|
propTraceParameters(( "ppenum=%p", ppenum ));
|
|
|
|
Lock(); fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto Exit;
|
|
}
|
|
|
|
if( !IsReadable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto Exit;
|
|
}
|
|
|
|
// ----------------------
|
|
// Create the Enumeration
|
|
// ----------------------
|
|
|
|
psa = (IStatArray*) new CStatArray( _np, &hr );
|
|
if( NULL == psa )
|
|
hr = STG_E_INSUFFICIENTMEMORY;
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
penum = new CEnumSTATPROPSTG( psa );
|
|
if( NULL == penum )
|
|
{
|
|
hr = STG_E_INSUFFICIENTMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
*ppenum = penum;
|
|
penum = NULL;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
RELEASE_INTERFACE( penum );
|
|
RELEASE_INTERFACE( psa );
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::SetTimes
|
|
//
|
|
// Synopsis: Set the given times on the underlying storage
|
|
//
|
|
// Arguments: [pctime] -- creation time
|
|
// [patime[ -- access time
|
|
// [pmtime] -- modify time
|
|
//
|
|
// Returns: S_OK or error.
|
|
//
|
|
// Notes:
|
|
// (non-simple only) Only the times supported by the
|
|
// underlying docfile implementation are
|
|
// supported.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::SetTimes(
|
|
FILETIME const * pctime,
|
|
FILETIME const * patime,
|
|
FILETIME const * pmtime)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::SetTimes" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
VDATEPTRIN_LABEL( pctime, FILETIME, errRet, hr );
|
|
VDATEPTRIN_LABEL( patime, FILETIME, errRet, hr );
|
|
VDATEPTRIN_LABEL( pmtime, FILETIME, errRet, hr );
|
|
|
|
propTraceParameters(( "pctime=%08x:%08x, patime=%08x:%08x, pmtime=%08x:%08x",
|
|
pctime->dwHighDateTime, pctime->dwLowDateTime,
|
|
patime->dwHighDateTime, patime->dwLowDateTime,
|
|
pmtime->dwHighDateTime, pmtime->dwLowDateTime ));
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
// -------------
|
|
// Set the Times
|
|
// -------------
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
hr = _pstgPropSet->SetElementTimes(
|
|
NULL,
|
|
pctime,
|
|
patime,
|
|
pmtime);
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::SetClass
|
|
//
|
|
// Synopsis: Sets the class of the property set.
|
|
//
|
|
// Arguments: [clsid] -- class id to set.
|
|
//
|
|
// Returns: S_OK or error.
|
|
//
|
|
// Notes: If non-simple, the underlying storage has SetClass
|
|
// called. Both simple and non-simple will have
|
|
// clsid set into the property set stream.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::SetClass(REFCLSID clsid)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
BOOL fLocked = FALSE;
|
|
DBGBUF(buf);
|
|
|
|
propXTrace( "CPropertyStorage::SetClass" );
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
GEN_VDATEREADPTRIN_LABEL(&clsid, CLSID, E_INVALIDARG, errRet, hr);
|
|
|
|
propTraceParameters(( "clsid=%s", DbgFmtId(clsid, buf) ));
|
|
|
|
// -------------
|
|
// Set the CLSID
|
|
// -------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsWriteable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
// Set it in the property set header
|
|
|
|
Status = PrSetPropertySetClassId(_np, &clsid);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
// And if this is an IStorage, set it there as well.
|
|
if (IsNonSimple())
|
|
{
|
|
hr = _pstgPropSet->SetClass(clsid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Stat
|
|
//
|
|
// Synopsis: Get STATPROPSETSTG about the property set.
|
|
//
|
|
// Arguments: [p] -- STATPROPSETSTG *
|
|
//
|
|
// Returns: S_OK if successful, error otherwise. On failure,
|
|
// *p is all zeros.
|
|
//
|
|
// Notes: See spec. Gets times from underlying storage or stream
|
|
// using IStorage or IStream ::Stat.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::Stat(STATPROPSETSTG * pstatpropsetstg)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propXTrace( "CPropertyStorage::Stat" )
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
// Validate inputs
|
|
|
|
VDATEPTROUT_LABEL(pstatpropsetstg, STATPROPSETSTG, errRet, hr);
|
|
|
|
propTraceParameters(( "STATPROPSETSTG *p = %08X", pstatpropsetstg ));
|
|
|
|
// ------------
|
|
// Get the Stat
|
|
// ------------
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
if( IsReverted() )
|
|
{
|
|
hr = STG_E_REVERTED;
|
|
goto errRet;
|
|
}
|
|
|
|
if( !IsReadable() )
|
|
{
|
|
hr = STG_E_ACCESSDENIED;
|
|
goto errRet;
|
|
}
|
|
|
|
ZeroMemory(pstatpropsetstg, sizeof(*pstatpropsetstg));
|
|
|
|
// returns mtime, ansi flag, clsid, fmtid
|
|
Status = PrQueryPropertySet(_np, pstatpropsetstg);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
STATSTG statstg;
|
|
|
|
hr = S_OK;
|
|
|
|
if( NULL != _pstgPropSet || NULL != _pstmPropSet )
|
|
{
|
|
if (IsNonSimple())
|
|
{
|
|
hr = _pstgPropSet->Stat(&statstg, STATFLAG_NONAME);
|
|
}
|
|
else
|
|
{
|
|
hr = _pstmPropSet->Stat(&statstg, STATFLAG_NONAME);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
pstatpropsetstg->mtime = statstg.mtime;
|
|
pstatpropsetstg->ctime = statstg.ctime;
|
|
pstatpropsetstg->atime = statstg.atime;
|
|
pstatpropsetstg->grfFlags = _grfFlags;
|
|
pstatpropsetstg->dwOSVersion = _dwOSVersion;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ZeroMemory(pstatpropsetstg, sizeof(*pstatpropsetstg));
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStatArray::CStatArray
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CStatArray::CStatArray(NTPROP np, HRESULT *phr)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ulKeyZero;
|
|
ULONG cpropAllocated;
|
|
|
|
_cpropActual = 0;
|
|
_cRefs = 1;
|
|
_psps = NULL;
|
|
|
|
do
|
|
{
|
|
// when *pkey == 0, *pcprop == MAXULONG, aprs == NULL and asps == NULL on input,
|
|
// *pcprop will be the total count of properties in the enumeration set. OLE needs to
|
|
// allocate memory and enumerate out of the cached PROPID+propname list.
|
|
|
|
ulKeyZero = 0;
|
|
_cpropActual = MAX_ULONG;
|
|
|
|
CoTaskMemFree( _psps );
|
|
_psps = NULL;
|
|
|
|
Status = PrEnumerateProperties(
|
|
np,
|
|
ENUMPROP_NONAMES,
|
|
&ulKeyZero,
|
|
&_cpropActual,
|
|
NULL, // aprs
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
cpropAllocated = _cpropActual + 1;
|
|
|
|
_psps = reinterpret_cast<STATPROPSTG*>
|
|
( CoTaskMemAlloc( sizeof(STATPROPSTG) * cpropAllocated ) );
|
|
if (_psps == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
ulKeyZero = 0;
|
|
Status = PrEnumerateProperties(
|
|
np,
|
|
0,
|
|
&ulKeyZero,
|
|
&cpropAllocated,
|
|
NULL, // aprs
|
|
_psps);
|
|
} while (NT_SUCCESS(Status) && cpropAllocated != _cpropActual);
|
|
|
|
*phr = NT_SUCCESS(Status) ? S_OK : DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStatArray::~CStatArray
|
|
//
|
|
// Synopsis: Deallocated the object's data.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CStatArray::~CStatArray()
|
|
{
|
|
if( NULL != _psps )
|
|
{
|
|
STATPROPSTG *psps = _psps;
|
|
|
|
while( _cpropActual )
|
|
{
|
|
CoTaskMemFree( psps->lpwstrName );
|
|
_cpropActual--;
|
|
psps++;
|
|
}
|
|
|
|
CoTaskMemFree( _psps );
|
|
}
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CStatArray:: QueryInterface/AddRef/Release
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CStatArray::QueryInterface( REFIID riid, void **ppvObject)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CStatArray::AddRef(void)
|
|
{
|
|
LONG lRet;
|
|
|
|
lRet = InterlockedIncrement(&_cRefs);
|
|
return(lRet);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CStatArray::Release(void)
|
|
{
|
|
LONG lRet;
|
|
|
|
lRet = InterlockedDecrement(&_cRefs);
|
|
|
|
if (lRet == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
else
|
|
if (lRet <0)
|
|
{
|
|
lRet = 0;
|
|
}
|
|
return(lRet);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStatArray::NextAt
|
|
//
|
|
// Synopsis: Read from the internal STATPROPSTG array.
|
|
//
|
|
// Effects: The cursor is passed in, and this function acts
|
|
// as a IEnumXX::Next would behave if the current cursor
|
|
// was [ipropNext].
|
|
//
|
|
// Arguments: [ipropNext] -- index of cursor to use
|
|
// [pspsDest] -- if NULL, emulate read's effect on cursor.
|
|
// if non-NULL, return data with cursor effect.
|
|
// [pceltFetched] -- buffer for count fetched
|
|
//
|
|
// Returns: STATUS_SUCCESS if successful, otherwise
|
|
// STATUS_INSUFFICIENT_RESOURCES.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
CStatArray::NextAt(ULONG ipropNext, STATPROPSTG *pspsDest, ULONG *pceltFetched)
|
|
{
|
|
ULONG ipropLastPlus1;
|
|
|
|
//
|
|
// Copy the requested number of elements from the cache
|
|
// (including strings, the allocation of which may fail.)
|
|
//
|
|
|
|
ipropLastPlus1 = ipropNext + *pceltFetched;
|
|
if (ipropLastPlus1 > _cpropActual)
|
|
{
|
|
ipropLastPlus1 = _cpropActual;
|
|
}
|
|
|
|
*pceltFetched = ipropLastPlus1 - ipropNext;
|
|
|
|
if (pspsDest != NULL)
|
|
return CopySTATPROPSTG(*pceltFetched, pspsDest, _psps + ipropNext);
|
|
else
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::CEnumSTATPROPSTG
|
|
//
|
|
// Synopsis: Constructor which is used by IEnumSTATPROPSTG::Clone.
|
|
//
|
|
// Arguments: [other] -- the CEnumSTATPROPSTG to copy
|
|
// [phr] -- the error code.
|
|
//
|
|
// Notes: Since the CStatArray actually contains the object this
|
|
// just adds to the ref count.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CEnumSTATPROPSTG::CEnumSTATPROPSTG(const CEnumSTATPROPSTG & other )
|
|
{
|
|
_ulSig = ENUMSTATPROPSTG_SIG;
|
|
_cRefs = 1;
|
|
|
|
_psa = other._psa;
|
|
_psa->AddRef();
|
|
|
|
_ipropNext = other._ipropNext;
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::~CEnumSTATPROPSTG
|
|
//
|
|
// Synopsis: Deallocated storage.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CEnumSTATPROPSTG::~CEnumSTATPROPSTG()
|
|
{
|
|
_ulSig = ENUMSTATPROPSTG_SIGDEL; // prevent another thread doing it - kinda
|
|
|
|
RELEASE_INTERFACE( _psa );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::QueryInterface
|
|
//
|
|
// Synopsis: Respond to IEnumSTATPROPSTG and IUnknown.
|
|
//
|
|
// Returns: S_OK or E_NOINTERFACE
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CEnumSTATPROPSTG::QueryInterface( REFIID riid, void **ppvObject)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
if (IsEqualIID(riid, IID_IEnumSTATPROPSTG))
|
|
{
|
|
*ppvObject = (IEnumSTATPROPSTG *)this;
|
|
AddRef();
|
|
}
|
|
else
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
*ppvObject = (IUnknown *)this;
|
|
AddRef();
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::AddRef
|
|
//
|
|
// Synopsis: Add 1 to ref count.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
ULONG CEnumSTATPROPSTG::AddRef(void)
|
|
{
|
|
long cRefs;
|
|
|
|
if (S_OK != Validate())
|
|
return(0);
|
|
|
|
cRefs = InterlockedIncrement(&_cRefs);
|
|
return(cRefs);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::Release
|
|
//
|
|
// Synopsis: Subtract 1 from ref count and delete if 0.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
ULONG CEnumSTATPROPSTG::Release(void)
|
|
{
|
|
LONG lRet;
|
|
|
|
if (S_OK != Validate())
|
|
return(0);
|
|
|
|
lRet = InterlockedDecrement(&_cRefs);
|
|
|
|
if (lRet == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
else
|
|
if (lRet <0)
|
|
{
|
|
lRet = 0;
|
|
}
|
|
return(lRet);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: CopySTATPROPSTG
|
|
//
|
|
// Synopsis: Copy out the range of elements from [pspsSrc] to
|
|
// [pspsDest].
|
|
//
|
|
// Arguments: [celt] -- count of elements to copy
|
|
// [pspsDest] -- where to copy to, always filled with
|
|
// zeros before anything else (helps cleanup
|
|
// case.)
|
|
//
|
|
// [pspsSrc] -- where to copy from
|
|
//
|
|
// Returns: STATUS_SUCCESS if ok, otherwise
|
|
// STATUS_INSUFFICIENT_RESOURCES in which case there
|
|
// may be pointers that need deallocating. Use
|
|
// CleanupSTATPROPSTG to do that.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
CopySTATPROPSTG(ULONG celt,
|
|
STATPROPSTG * pspsDest,
|
|
const STATPROPSTG * pspsSrc)
|
|
{
|
|
memset(pspsDest, 0, sizeof(*pspsDest) * celt);
|
|
|
|
while (celt)
|
|
{
|
|
*pspsDest = *pspsSrc;
|
|
|
|
if (pspsSrc->lpwstrName != NULL)
|
|
{
|
|
pspsDest->lpwstrName = reinterpret_cast<OLECHAR*>
|
|
( CoTaskMemAlloc( sizeof(OLECHAR)*( 1 + ocslen(pspsSrc->lpwstrName) ) ));
|
|
if (pspsDest->lpwstrName != NULL)
|
|
{
|
|
ocscpy(pspsDest->lpwstrName,
|
|
pspsSrc->lpwstrName);
|
|
}
|
|
else
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
celt--;
|
|
pspsDest++;
|
|
pspsSrc++;
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::Next
|
|
//
|
|
// Synopsis: Get the next [celt] STATPROPSTGs from the enumerator.
|
|
//
|
|
// Arguments: [celt] -- count requested.
|
|
// [rgelt] -- where to return them
|
|
// [pceltFetched] -- buffer for returned-count.
|
|
// if pceltFetched==NULL && celt != 1 -> STG_E_INVALIDPARAMETER
|
|
// if pceltFetched!=NULL && celt == 0 -> S_OK
|
|
//
|
|
// Returns: S_OK if successful, otherwise error.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CEnumSTATPROPSTG::Next(
|
|
ULONG celt,
|
|
STATPROPSTG * rgelt,
|
|
ULONG * pceltFetched)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
ULONG celtFetched = celt;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
// Validate the inputs
|
|
|
|
if (NULL == pceltFetched)
|
|
{
|
|
if (celt != 1)
|
|
return(STG_E_INVALIDPARAMETER);
|
|
}
|
|
else
|
|
{
|
|
VDATEPTROUT( pceltFetched, ULONG );
|
|
*pceltFetched = 0;
|
|
}
|
|
|
|
if( 0 == celt )
|
|
return( S_OK );
|
|
|
|
if( !IsValidPtrOut(rgelt, celt * sizeof(rgelt[0])) )
|
|
return( E_INVALIDARG );
|
|
|
|
|
|
// -----------------------
|
|
// Perform the enumeration
|
|
// -----------------------
|
|
|
|
if (celt == 0)
|
|
return(hr);
|
|
|
|
Status = _psa->NextAt(_ipropNext, rgelt, &celtFetched);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
_ipropNext += celtFetched;
|
|
|
|
if (pceltFetched != NULL)
|
|
*pceltFetched = celtFetched;
|
|
|
|
hr = celtFetched == celt ? S_OK : S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
return(hr);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::Skip
|
|
//
|
|
// Synopsis: Skip the next [celt] elements in the enumeration.
|
|
//
|
|
// Arguments: [celt] -- number of elts to skip
|
|
//
|
|
// Returns: S_OK if skipped [celt] elements
|
|
// S_FALSE if skipped < [celt] elements
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CEnumSTATPROPSTG::Skip(ULONG celt)
|
|
{
|
|
HRESULT hr;
|
|
ULONG celtFetched = celt;
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
_psa->NextAt(_ipropNext, NULL, &celtFetched);
|
|
|
|
_ipropNext += celtFetched;
|
|
|
|
return celtFetched == celt ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::Reset
|
|
//
|
|
// Synopsis: Set cursor to beginnging of enumeration.
|
|
//
|
|
// Returns: S_OK otherwise STG_E_INVALIDHANDLE.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CEnumSTATPROPSTG::Reset()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
_ipropNext = 0;
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::Clone
|
|
//
|
|
// Synopsis: Creates an IEnumSTATPROPSTG with same cursor
|
|
// as this.
|
|
//
|
|
// Arguments: S_OK or error.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CEnumSTATPROPSTG::Clone(IEnumSTATPROPSTG ** ppenum)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
// Validate the input
|
|
|
|
VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
|
|
*ppenum = NULL;
|
|
|
|
// --------------------
|
|
// Clone the enumerator
|
|
// --------------------
|
|
|
|
*ppenum = new CEnumSTATPROPSTG( *this );
|
|
|
|
if( NULL == *ppenum )
|
|
hr = STG_E_INSUFFICIENTMEMORY;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: Lock & Unlock
|
|
//
|
|
// Synopsis: This methods take and release the CPropertyStorage's
|
|
// critical section.
|
|
//
|
|
// Inputs: none
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CPropertyStorage::Lock(void)
|
|
{
|
|
#ifndef _MAC
|
|
DfpAssert (_fInitCriticalSection);
|
|
EnterCriticalSection( &_CriticalSection );
|
|
#endif
|
|
|
|
#if DBG
|
|
_cLocks++;
|
|
#endif
|
|
|
|
}
|
|
|
|
VOID
|
|
CPropertyStorage::Unlock()
|
|
{
|
|
#if DBG
|
|
--_cLocks;
|
|
DfpAssert( 0 <= _cLocks );
|
|
#endif
|
|
|
|
#ifndef _MAC
|
|
DfpAssert (_fInitCriticalSection);
|
|
LeaveCriticalSection( &_CriticalSection );
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: CPropertyStorage::ProbeStreamToDetermineIfWriteable
|
|
//
|
|
// Synopsis: Probes the IStream which holds the property set to see if it
|
|
// can be written. Ordinarily we know whether or not a stream
|
|
// is writeable either because we were given the grfMode or
|
|
// because we Stat-ed it out of the stream. But this code
|
|
// was added for the case where IStream::Stat returns zero
|
|
// for the grfMode but it's actually writeable (this happens
|
|
// with CreateStreamOnHGlobal). So a grfMode of zero is a hint
|
|
// that the IStream may not support that value in the Stat.
|
|
//
|
|
// This method should be called lazily the first time a modify
|
|
// operation is called on the property set, because it will
|
|
// cause an update to the last-modify time of the stream.
|
|
//
|
|
// Inputs: none
|
|
//
|
|
// Returns: TRUE if the stream is writeable. Also sets
|
|
// _fExplicitelyProbedForWriteAccess so that we don't call this
|
|
// twice.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CPropertyStorage::ProbeStreamToDetermineIfWriteable()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fWriteable = FALSE;
|
|
BYTE FirstByte;
|
|
LARGE_INTEGER liZero = {0};
|
|
|
|
propITrace( "CPropertyStorage::ProbeStreamToDetermineIfWriteable" );
|
|
AssertLocked();
|
|
DfpAssert( !_fExplicitelyProbedForWriteAccess );
|
|
|
|
// This routine is only called once
|
|
|
|
_fExplicitelyProbedForWriteAccess = TRUE;
|
|
|
|
// Read then write a byte
|
|
|
|
hr = _pstmPropSet->Read( &FirstByte, 1, NULL );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = _pstmPropSet->Seek( liZero, STREAM_SEEK_SET, NULL );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = _pstmPropSet->Write( &FirstByte, 1, NULL );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// If the write worked, then this stream is really STGM_READWRITE
|
|
|
|
fWriteable = TRUE;
|
|
_grfMode |= STGM_READWRITE;
|
|
|
|
Exit:
|
|
|
|
propDbg((DEB_ITRACE, "Property Set %p %s writeable (hr=%08x)\n",
|
|
this, fWriteable?"is":"isn't", hr ));
|
|
|
|
return( fWriteable );
|
|
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Member: InitializeOnCreateOrOpen
|
|
//
|
|
// Synopsis: This routine is called during the creation or opening
|
|
// of a Property Storage, and initializes everything
|
|
// it can without being concerned about whether this
|
|
// is a simple or non-simple property set.
|
|
//
|
|
// Inputs: [DWORD] grfFlags (in)
|
|
// From the PROPSETFLAG_* enumeration.
|
|
// [DWORD] grfMode (in)
|
|
// From the STGM_* enumeration.
|
|
// [REFFMTID] rfmtid (in)
|
|
// The ID of the property set.
|
|
// [BOOL] fCreate (in)
|
|
// Distinguishes Create from Open.
|
|
//
|
|
// Returns: [HRESULT]
|
|
//
|
|
// Effects: _grfFlags, _grfMode, _fUserDefinedProperties,
|
|
// and g_ReservedMemory.
|
|
//
|
|
//+-----------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
CPropertyStorage::InitializeOnCreateOrOpen(
|
|
DWORD grfFlags,
|
|
DWORD grfMode,
|
|
REFFMTID rfmtid,
|
|
BOOL fCreate )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
propITrace( "CPropertyStorage::InitializeOnCreateOrOpen" );
|
|
AssertLocked();
|
|
|
|
// If the caller didn't give us a grfMode, stat for it.
|
|
|
|
if( 0 == grfMode )
|
|
{
|
|
STATSTG statstg;
|
|
DfpAssert( NULL != _pstgPropSet || NULL != _pstmPropSet );
|
|
|
|
if( NULL != _pstgPropSet )
|
|
hr = _pstgPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
else
|
|
hr = _pstmPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
|
|
if( FAILED(hr) ) goto Exit;
|
|
grfMode = statstg.grfMode;
|
|
}
|
|
|
|
// Validate that grfFlags is within the enumeration.
|
|
if (grfFlags & ~(PROPSETFLAG_ANSI | PROPSETFLAG_NONSIMPLE | PROPSETFLAG_UNBUFFERED | PROPSETFLAG_CASE_SENSITIVE))
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = CheckFlagsOnCreateOrOpen( fCreate, grfMode );
|
|
if (hr != S_OK)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Store the grfFlags & grfMode.
|
|
_grfFlags = grfFlags;
|
|
|
|
_grfMode = grfMode;
|
|
|
|
// Is this the special-case second-section property set?
|
|
_fUserDefinedProperties = ( rfmtid == FMTID_UserDefinedProperties ) ? TRUE : FALSE;
|
|
|
|
if (fCreate
|
|
&&
|
|
(_grfFlags & PROPSETFLAG_ANSI) )
|
|
{
|
|
_usCodePage = static_cast<USHORT>(GetACP());
|
|
}
|
|
|
|
// Initialize the global reserved memory (to prevent problems
|
|
// in low-memory conditions).
|
|
|
|
if (S_OK != (hr = g_ReservedMemory.Init()))
|
|
goto Exit;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
|
|
} // CPropertyStorage::InitializeOnCreate()
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Create( IStream * ...
|
|
//
|
|
// Synopsis: This method creates an IPropertyStorage on a
|
|
// given *Stream*. It is therefore a simple property
|
|
// set. The given Stream is addref-ed.
|
|
//
|
|
// Arguments: [IStream*] pstm
|
|
// The Stream which will hold the serialized property set.
|
|
// [REFFMTID] rfmtid
|
|
// The ID of the property set.
|
|
// [const CLSID*]
|
|
// The COM object which can interpret the property set.
|
|
// [DWORD] grfFlags
|
|
// From the PROPSETFLAG_* enumeration.
|
|
// [DWORD] grfMode
|
|
// From the STGM_* enumeration. If 0, we use Stat.
|
|
// [HRESULT*]
|
|
// The return code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::Create(
|
|
IStream *pstm,
|
|
REFFMTID rfmtid,
|
|
const CLSID *pclsid,
|
|
DWORD grfFlags,
|
|
DWORD grfMode
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fCreated = FALSE;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propITrace( "CPropertyStorage::Create(IStream*)" );
|
|
propTraceParameters(( "pstm=%p, rfmtid=%s, grfFlags=%s, grfMode=%s, fDelete=%s",
|
|
pstm,
|
|
static_cast<const char*>(CStringize(rfmtid)),
|
|
static_cast<const char*>(CStringize(*pclsid)),
|
|
static_cast<const char*>(CStringize(SGrfFlags(grfFlags))),
|
|
static_cast<const char*>(CStringize(SGrfMode(grfMode))) ));
|
|
|
|
// Save and addref the Stream.
|
|
|
|
_pstmPropSet = pstm;
|
|
_pstmPropSet->AddRef();
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
// Initialize this object
|
|
|
|
DfpAssert( !(PROPSETFLAG_NONSIMPLE & grfFlags ));
|
|
hr = InitializeOnCreateOrOpen( grfFlags, grfMode, rfmtid,
|
|
TRUE ); // => Create
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
DfpAssert( !IsNonSimple() );
|
|
|
|
// Initialize the Stream.
|
|
|
|
hr = InitializePropertyStream( &rfmtid, pclsid, CREATE_PROPSTREAM );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
// On error, remove our reference to the Stream.
|
|
if( FAILED(hr) )
|
|
{
|
|
_pstmPropSet->Release();
|
|
_pstmPropSet = NULL;
|
|
|
|
propDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::Create(IStream*)"
|
|
" hr=%08X\n", this, hr));
|
|
}
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyStorage::Create( IStream *, ...
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Create( IStorage *, ...
|
|
//
|
|
// Synopsis: This method creates an IPropertyStorage on a
|
|
// given *Storage*. It is therefore a non-simple property
|
|
// set. The Storage is addref-ed.
|
|
//
|
|
// Arguments: [IStorage*] pstm
|
|
// The Storage which will hold the serialized property set.
|
|
// [REFFMTID] rfmtid
|
|
// The ID of the property set.
|
|
// [const CLSID*]
|
|
// The COM object which can interpret the property set.
|
|
// [DWORD] grfFlags
|
|
// From the PROPSETFLAG_* enumeration.
|
|
// [HRESULT*]
|
|
// The return code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::Create(
|
|
IStorage *pstg,
|
|
REFFMTID rfmtid,
|
|
const CLSID *pclsid,
|
|
DWORD grfFlags,
|
|
DWORD grfMode
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fCreated = FALSE;
|
|
BOOL fLocked = FALSE;
|
|
STATSTG statstg = { NULL };
|
|
|
|
propITrace( "CPropertyStorage::Create(IStorage*)" );
|
|
propTraceParameters(( "pstg=%p, rfmtid=%s, grfFlags=%s, grfMode=%s, fDelete=%s",
|
|
pstg,
|
|
static_cast<const char*>(CStringize(rfmtid)),
|
|
static_cast<const char*>(CStringize(*pclsid)),
|
|
static_cast<const char*>(CStringize(SGrfFlags(grfFlags))),
|
|
static_cast<const char*>(CStringize(SGrfMode(grfMode))) ));
|
|
|
|
// Save the given Storage.
|
|
|
|
_pstgPropSet = pstg;
|
|
_pstgPropSet->AddRef();
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
// Initialize this object.
|
|
|
|
DfpAssert( grfFlags & PROPSETFLAG_NONSIMPLE );
|
|
hr = InitializeOnCreateOrOpen( grfFlags, grfMode, rfmtid,
|
|
TRUE ); // => Create
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
DfpAssert( IsNonSimple() );
|
|
|
|
// Create the "CONTENTS" stream. Mask out the STGM_TRANSACTED
|
|
// bit because we don't support it.
|
|
|
|
hr = _pstgPropSet->CreateStream(g_oszPropertyContentsStreamName,
|
|
GetChildCreateMode() & ~STGM_TRANSACTED,
|
|
0, 0, &_pstmPropSet);
|
|
if (FAILED(hr)) goto Exit;
|
|
fCreated = TRUE;
|
|
|
|
// Initialize the CONTENTS Stream.
|
|
|
|
hr = InitializePropertyStream( &rfmtid, pclsid, CREATE_PROPSTREAM );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// In the transacted case, ensure that the contents
|
|
// stream is actually published right away.
|
|
// The logic is this ... if you have a storage and create a transacted
|
|
// child storage, that child storage is complete and intact to the parent.
|
|
// If you then revert the child, or make no changes to it, the parent still
|
|
// has a valid (albeit empty) storage. Now, say you do the same thing, but
|
|
// the transacted child is a property set. As it stands at this point in this
|
|
// method, the parent can only see an empty storage. For it to see a valid
|
|
// (empty) property set child, it must see the Contents stream, along with its default
|
|
// data (header, codepage, etc.). In order to make this happen, we must
|
|
// commit (not just flush) this storage that holds a property set.
|
|
//
|
|
// There's one more complication. Even if this is a direct mode property
|
|
// set, the _pstgPropSet may be transacted nonetheless, for the purpose of
|
|
// robustness (this happens in NFF). So, we need to commit not if
|
|
// _grfMode is transacted, but if _pstgPropSet says that it's transacted.
|
|
|
|
hr = _pstgPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
if( STGM_TRANSACTED & statstg.grfMode )
|
|
{
|
|
hr = Commit(STGC_DEFAULT);
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
}
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
else if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = PrFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
// On error, remove our reference to the Storage.
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
_pstgPropSet->Release();
|
|
_pstgPropSet = NULL;
|
|
|
|
// Also, delete the "CONTENTS" stream.
|
|
if( fCreated )
|
|
pstg->DestroyElement( g_oszPropertyContentsStreamName );
|
|
|
|
propDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::Create(IStorage*)"
|
|
" hr=%08X\n", this, hr));
|
|
}
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyStorage::Create( IStorage *, ...
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Open( IStream * ...
|
|
//
|
|
// Synopsis: This method opens an IPropertyStorage on a
|
|
// given *Stream*. It is therefore a simple property
|
|
// set. The Stream is addref-ed.
|
|
//
|
|
// Arguments: [IStream*] pstm
|
|
// The Stream which will hold the serialized property set.
|
|
// [REFFMTID] rfmtid
|
|
// The ID of the property set.
|
|
// [DWORD] grfFlags
|
|
// From the PROPSETFLAG_ enumeration. Only the
|
|
// _UNBUFFERED flag is relevant _ANSI and
|
|
// _NONSIMPLE are inferred from the property set.
|
|
// [BOOL] fDelete
|
|
// If TRUE, the property set is actually to be deleted,
|
|
// rather than opened (this is used for the special-case
|
|
// "UserDefined" property set).
|
|
// [HRESULT*]
|
|
// The return code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::Open(
|
|
IStream *pstm,
|
|
REFFMTID rfmtid,
|
|
DWORD grfFlags,
|
|
DWORD grfMode,
|
|
BOOL fDelete
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propITrace( "CPropertyStorage::Open(IStream*)" );
|
|
propTraceParameters(( "pstm=%p, rfmtid=%s, grfFlags=%s, grfMode=%s, fDelete=%s",
|
|
pstm,
|
|
static_cast<const char*>(CStringize(rfmtid)),
|
|
static_cast<const char*>(CStringize(SGrfFlags(grfFlags))),
|
|
static_cast<const char*>(CStringize(SGrfMode(grfMode))),
|
|
fDelete ? "True":"False" ));
|
|
|
|
// Keep a copy of the Stream.
|
|
|
|
_pstmPropSet = pstm;
|
|
_pstmPropSet->AddRef();
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
// Initialize this object.
|
|
|
|
hr = InitializeOnCreateOrOpen( grfFlags,
|
|
grfMode,
|
|
rfmtid,
|
|
FALSE ); // => Open
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Only simple sections may be deleted (really, only the
|
|
// second section of the DocumentSummaryInformation property
|
|
// set may be deleted in this way).
|
|
|
|
DfpAssert( !fDelete || !IsNonSimple() );
|
|
|
|
// Initialize the property set Stream.
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// sets up _usCodePage
|
|
hr = InitializePropertyStream(
|
|
&rfmtid,
|
|
NULL,
|
|
fDelete ? DELETE_PROPSTREAM : OPEN_PROPSTREAM );
|
|
|
|
}
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
// On error, remove our reference to the Stream.
|
|
if( FAILED(hr) )
|
|
{
|
|
_pstmPropSet->Release();
|
|
_pstmPropSet = NULL;
|
|
|
|
propDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::Open(IStream*)"
|
|
" hr=%08X\n", this, hr));
|
|
}
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyStorage::Open( IStream *, ...
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Open( IStorage * ...
|
|
//
|
|
// Synopsis: This method opens an IPropertyStorage on a
|
|
// given *Storage*. It is therefore a non-simple property
|
|
// set. The Storage is addref-ed.
|
|
//
|
|
// Arguments: [IStorage*] pstg
|
|
// The Storage which will hold the serialized property set.
|
|
// [REFFMTID] rfmtid
|
|
// The ID of the property set.
|
|
// [DWORD] grfFlags
|
|
// From the PROPSETFLAG_ enumeration. Only the
|
|
// _UNBUFFERED flag is relevant _ANSI and
|
|
// _NONSIMPLE are inferred from the property set.
|
|
// [HRESULT*]
|
|
// The return code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::Open(
|
|
IStorage *pstg,
|
|
REFFMTID rfmtid,
|
|
DWORD grfFlags,
|
|
DWORD grfMode
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CPropSetName psn(rfmtid);
|
|
USHORT createprop = 0L;
|
|
BOOL fLocked = FALSE;
|
|
|
|
propITrace( "CPropertyStorage::Open(IStorage*)" );
|
|
propTraceParameters(( "pstg=%p, rfmtid=%s, grfFlags=%s, grfMode=%s",
|
|
pstg,
|
|
static_cast<const char*>(CStringize(rfmtid)),
|
|
static_cast<const char*>(CStringize(SGrfFlags(grfFlags))),
|
|
static_cast<const char*>(CStringize(SGrfMode(grfMode))) ));
|
|
|
|
|
|
// Keep a copy of the Storage
|
|
|
|
_pstgPropSet = pstg;
|
|
_pstgPropSet->AddRef();
|
|
|
|
Lock();
|
|
fLocked = TRUE;
|
|
|
|
// Initialize this object.
|
|
|
|
hr = InitializeOnCreateOrOpen( grfFlags,
|
|
grfMode,
|
|
rfmtid,
|
|
FALSE ); // => Open
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
_grfFlags |= PROPSETFLAG_NONSIMPLE;
|
|
|
|
// Open the CONTENTS stream. Mask out the STGM_TRANSACTED bit
|
|
// because it causes an error on Mac OLE2.
|
|
|
|
hr = _pstgPropSet->OpenStream( g_oszPropertyContentsStreamName,
|
|
0,
|
|
GetChildOpenMode() & ~STGM_TRANSACTED,
|
|
0,
|
|
&_pstmPropSet );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// Load the property set Stream.
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// sets up _usCodePage
|
|
hr = InitializePropertyStream(
|
|
&rfmtid,
|
|
NULL,
|
|
OPEN_PROPSTREAM );
|
|
|
|
}
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
// On error, remove our reference to the Storage.
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
_pstgPropSet->Release();
|
|
_pstgPropSet = NULL;
|
|
|
|
if( NULL != _pstmPropSet )
|
|
{
|
|
_pstmPropSet->Release();
|
|
_pstmPropSet = NULL;
|
|
}
|
|
|
|
propDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::Open(IStorage*)"
|
|
" hr=%08X\n", this, hr));
|
|
}
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyStorage::Open( IStorage *, ...
|
|
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::CreateMappedStream
|
|
//
|
|
// Synopsis: Create a IMappedStream object on an IStream.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: This method calls QI through the PropSet Stream to see if
|
|
// a mapped stream exists. If it doesn't then this
|
|
// method creates a IMappedStream which maps
|
|
// an IStream.
|
|
//
|
|
//+----------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::CreateMappedStream()
|
|
{
|
|
HRESULT hr;
|
|
|
|
DfpAssert( NULL != _pstmPropSet );
|
|
DfpAssert( NULL == _ms );
|
|
AssertLocked();
|
|
propITrace( "CPropertyStorage::CreateMappedStream" );
|
|
|
|
// QI the property set's IStream, if asked to do so, for an IMappedstream.
|
|
if( MAPPED_STREAM_QI == _fMSOpts )
|
|
{
|
|
// We got a mapped stream, so we're done.
|
|
hr = _pstmPropSet->QueryInterface(IID_IMappedStream,(void**)&_ms);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
propDbg(( DEB_INFO, "Using QI-ed IMappedStream\n" ));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Either we couldn't get a mapped stream from the IStream, or
|
|
// we were told not to ask for one. In either case, we'll
|
|
// create our own.
|
|
|
|
hr = S_OK;
|
|
|
|
_ms = (IMappedStream *) new CSSMappedStream( _pstmPropSet );
|
|
if( NULL == _ms )
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
propDbg(( DEB_INFO, "Using CSSMappedStream\n" ));
|
|
|
|
Exit:
|
|
return( hr );
|
|
}
|
|
|
|
VOID
|
|
CPropertyStorage::DeleteMappedStream()
|
|
{
|
|
AssertLocked();
|
|
|
|
if (NULL != _ms) {
|
|
_ms->Release();
|
|
}
|
|
_ms = NULL;
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
HRESULT
|
|
CPropertyStorage::UseNTFS4Streams( BOOL fUseNTFS4Streams )
|
|
{
|
|
IStorageTest *pPropStgTest = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if( NULL != _pstmPropSet )
|
|
hr = _pstmPropSet->QueryInterface( IID_IStorageTest,
|
|
reinterpret_cast<void**>(&pPropStgTest) );
|
|
else if( NULL != _pstgPropSet )
|
|
hr = _pstgPropSet->QueryInterface( IID_IStorageTest,
|
|
reinterpret_cast<void**>(&pPropStgTest) );
|
|
else
|
|
hr = STG_E_NOMOREFILES;
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = pPropStgTest->UseNTFS4Streams( fUseNTFS4Streams );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
Exit:
|
|
|
|
if( NULL != pPropStgTest )
|
|
pPropStgTest->Release();
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyStorage::UseNTFS4Streams()
|
|
#endif // #if DBG
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CPropertyStorage::GetFormatVersion (IStorageTest) **DBG**
|
|
//
|
|
// Get the property set's wFormatVersion field.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CPropertyStorage::GetFormatVersion(WORD *pw)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
CPropertySetStream *pPropertySetStream = (CPropertySetStream*) _np;
|
|
|
|
status = pPropertySetStream->Lock( TRUE );
|
|
if( !NT_SUCCESS(status) ) goto Exit;
|
|
|
|
pPropertySetStream->ReOpen( &status );
|
|
if( !NT_SUCCESS(status) ) goto Exit;
|
|
|
|
*pw = pPropertySetStream->GetFormatVersion();
|
|
status = STATUS_SUCCESS;
|
|
|
|
Exit:
|
|
|
|
if( !NT_SUCCESS(status) )
|
|
hr = DfpNtStatusToHResult(status);
|
|
|
|
pPropertySetStream->Unlock();
|
|
return( hr );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CPropertyStorage::SimulateLowMemory (IStorageTest) **DBG**
|
|
//
|
|
// Forcable turn on the low-memory support in the IMappedStream implementation.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CPropertyStorage::SimulateLowMemory( BOOL fSimulate )
|
|
{
|
|
IStorageTest *pPropStgTest = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if( NULL != _pstmPropSet )
|
|
hr = _pstmPropSet->QueryInterface( IID_IStorageTest,
|
|
reinterpret_cast<void**>(&pPropStgTest) );
|
|
else if( NULL != _pstgPropSet )
|
|
hr = _pstgPropSet->QueryInterface( IID_IStorageTest,
|
|
reinterpret_cast<void**>(&pPropStgTest) );
|
|
else
|
|
hr = STG_E_NOMOREFILES;
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = pPropStgTest->SimulateLowMemory( fSimulate );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
Exit:
|
|
|
|
if( NULL != pPropStgTest )
|
|
pPropStgTest->Release();
|
|
|
|
return( hr );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CPropertyStorage::GetLockCount()
|
|
{
|
|
return( E_NOTIMPL );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CPropertyStorage::IsDirty (IStorageTest) **DBG**
|
|
//
|
|
// Determine if the IMappedStream is dirty.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CPropertyStorage::IsDirty()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IStorageTest *ptest = NULL;
|
|
|
|
if( NULL == _ms )
|
|
{
|
|
hr = S_FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = _ms->QueryInterface( IID_IStorageTest, reinterpret_cast<void**>(&ptest) );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = ptest->IsDirty();
|
|
|
|
Exit:
|
|
|
|
RELEASE_INTERFACE(ptest);
|
|
return( hr );
|
|
|
|
}
|
|
#endif // #if DBG
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CPropertyStorage::ValidateVTs
|
|
//
|
|
// Validate the VTs in an array of PropVariants. If we see a type we don't
|
|
// understand, return error_not_supported.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::ValidateVTs( ULONG cprops, const PROPVARIANT rgpropvar[] )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
propITrace( "CPropertyStorage::ValidateVTs" );
|
|
propTraceParameters(( "cprops=%d, rgpropvar=%p", cprops, rgpropvar ));
|
|
|
|
for( ULONG i = 0; i < cprops; i++ )
|
|
{
|
|
if( !IsSupportedVarType( rgpropvar[i].vt ) )
|
|
{
|
|
propDbg(( DEB_IWARN, "Unsupported VarType in ValidateVTs: 0x%x\n", rgpropvar[i].vt ));
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
propSuppressExitErrors();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
}
|