mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2039 lines
51 KiB
2039 lines
51 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "pch.cxx"
|
|
|
|
DECLARE_INFOLEVEL(prop, DEB_ERROR)
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CCoTaskAllocator::Allocate, Free.
|
|
//
|
|
// Synopsis: Allocation routines called by RtlQueryProperties.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CCoTaskAllocator g_CoTaskAllocator;
|
|
|
|
void *
|
|
CCoTaskAllocator::Allocate(ULONG cbSize)
|
|
{
|
|
return(CoTaskMemAlloc(cbSize));
|
|
}
|
|
|
|
void
|
|
CCoTaskAllocator::Free(void *pv)
|
|
{
|
|
CoTaskMemFree(pv);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::CPropertyStorage
|
|
//
|
|
// Synopsis: Constructor used to create a property storage on disk.
|
|
//
|
|
// Arguments: [pstg] -- storage object to be parent
|
|
// [rfmtid] -- format id for new property set.
|
|
// [pclsid] -- pointer to class id
|
|
// [grfFlags] -- flags
|
|
// [grfMode] -- mode
|
|
// [phr] -- HRESULT assumed to be STG_E_INSUFFICIENTMEMORY
|
|
// on entry. Will be HRESULT reflecting result on exit.
|
|
//
|
|
// Notes: Get a CPropertySetStream initialized with the correct
|
|
// type of map (i.e. docfile or native.)
|
|
// If non-simple mode, create a storage with name derived
|
|
// from rfmtid and then a contents sub-stream.
|
|
// If simple mode, create a stream of a name derived from
|
|
// rfmtid.
|
|
//
|
|
// Does not clean up on failure: this is done by the
|
|
// destructor.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CPropertyStorage::CPropertyStorage(
|
|
IPrivateStorage *pprivstg,
|
|
REFFMTID rfmtid,
|
|
const CLSID *pclsid,
|
|
DWORD grfFlags,
|
|
DWORD grfMode,
|
|
HRESULT *phr)
|
|
{
|
|
HRESULT & hr = *phr;
|
|
CPropSetName psn(rfmtid); // acts as Probe(&rfmtid, sizeof(rfmtid));
|
|
BOOL fCreated = FALSE;
|
|
IStorage *pstg = pprivstg->GetStorage();
|
|
|
|
Initialize();
|
|
|
|
if (grfFlags & PROPSETFLAG_NONSIMPLE)
|
|
{
|
|
hr = STG_E_UNIMPLEMENTEDFUNCTION;
|
|
PROPASSERT(FALSE && "Unsupported function in reference called!\n" );
|
|
return;
|
|
}
|
|
|
|
if (grfFlags & ~PROPSETFLAG_ANSI)
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
return;
|
|
}
|
|
|
|
// check for any mode flags disallowed in Create.
|
|
if (grfMode & (STGM_PRIORITY | STGM_CONVERT |
|
|
STGM_SIMPLE | STGM_DELETEONRELEASE))
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
return;
|
|
}
|
|
|
|
_grfFlags = grfFlags;
|
|
_grfAccess = 3 & grfMode;
|
|
_grfShare = 0xF0 & grfMode;
|
|
|
|
// Is this the special-case second-section property set?
|
|
_fUserDefinedProperties = ( rfmtid == FMTID_UserDefinedProperties ) ? TRUE : FALSE;
|
|
|
|
if (_grfAccess != STGM_READWRITE)
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
return;
|
|
}
|
|
|
|
if (_grfFlags & PROPSETFLAG_ANSI)
|
|
{
|
|
_usCodePage = GetACP();
|
|
}
|
|
|
|
int i=0;
|
|
while (i<=1)
|
|
{
|
|
// Create the property set stream in pstg.
|
|
// The second section of the DocumentSummaryInformation Property Set
|
|
// is a special-case.
|
|
|
|
if( IsEqualGUID( rfmtid, FMTID_UserDefinedProperties ))
|
|
{
|
|
hr = _CreateDocumentSummary2Stream( pstg, psn, grfMode, &fCreated );
|
|
}
|
|
else
|
|
{
|
|
hr = pstg->CreateStream(psn.GetPropSetName(), grfMode, 0, 0, &_pstmPropSet);
|
|
if( hr == S_OK )
|
|
fCreated = TRUE;
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::CPropertyStorage"
|
|
" - CreateStream(%ls) attempt %d, hr=%08X\n", this, psn.GetPropSetName(), i+1, hr));
|
|
|
|
|
|
if (hr != STG_E_FILEALREADYEXISTS)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
if (i == 0 && (grfMode & STGM_CREATE) == STGM_CREATE)
|
|
{
|
|
pstg->DestroyElement(psn.GetPropSetName());
|
|
}
|
|
} // if (hr == S_OK) ... else
|
|
|
|
i++;
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = InitializePropertyStream(CREATEPROP_CREATE,
|
|
&rfmtid,
|
|
pclsid);
|
|
}
|
|
|
|
if (hr != S_OK && fCreated)
|
|
{
|
|
//
|
|
// if we fail after creating the property set in storage, cleanup.
|
|
//
|
|
pstg->DestroyElement(psn.GetPropSetName());
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::_CreateDocumentSummary2Stream
|
|
//
|
|
// Synopsis: Open the "DocumentSummaryInformation" stream, creating
|
|
// it if necessary.
|
|
//
|
|
// Arguments: [pstg] -- container storage
|
|
// [psn] -- the property set name
|
|
// [grfMode] -- mode of the property set
|
|
// [fCreated] -- TRUE if Stream is created, FALSE if opened.
|
|
//
|
|
// Notes: This special case is necessary because this property set
|
|
// is the only one in which we support more than one section.
|
|
// For this property set, if the caller Creates the second
|
|
// Section, we must not *Create* the Stream, because that would
|
|
// lost the first Section. So, we must open it.
|
|
//
|
|
// This routine is only called when creating the second
|
|
// Section. The first Section is created normally (note
|
|
// that if the client creates the first section, the second
|
|
// section is lost).
|
|
//
|
|
// Also note that it may not be possible to open the Stream,
|
|
// since it may already be opened. This is significant
|
|
// because it may not be obvious to the caller. I.e.,
|
|
// to a client of IPropertyStorage, the 2 sections are
|
|
// distinct property sets, and you would think that you could
|
|
// open them for simultaneous write.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::_CreateDocumentSummary2Stream( IStorage * pstg,
|
|
CPropSetName & psn,
|
|
DWORD grfMode,
|
|
BOOL * pfCreated )
|
|
{
|
|
|
|
HRESULT hr;
|
|
DWORD grfOpenMode = grfMode & ~(STGM_CREATE | STGM_CONVERT);
|
|
|
|
*pfCreated = FALSE;
|
|
|
|
hr = pstg->OpenStream( psn.GetPropSetName(), NULL, grfOpenMode, 0L, &_pstmPropSet );
|
|
|
|
// If the file wasn't there, try a create.
|
|
|
|
if( hr == STG_E_FILENOTFOUND )
|
|
{
|
|
hr = pstg->CreateStream(psn.GetPropSetName(), grfMode, 0, 0, &_pstmPropSet);
|
|
|
|
if( SUCCEEDED( hr ))
|
|
{
|
|
*pfCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
return( hr );
|
|
|
|
} // CPropertyStorage::_CreateDocumentSummary2Stream()
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::CPropertyStorage
|
|
//
|
|
// Synopsis: Constructor used to open a property storage on disk.
|
|
//
|
|
// Arguments: [pstg] -- container storage
|
|
// [rfmtid] -- FMTID of property set to open
|
|
// [grfMode] -- mode of the property set
|
|
// [fDelete] -- Delete this property set from its stream
|
|
// [phr] -- HRESULT returned here.
|
|
//
|
|
// Notes: Does not clean up on failure: this is done by the
|
|
// destructor.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CPropertyStorage::CPropertyStorage(
|
|
IPrivateStorage *pprivstg,
|
|
REFFMTID rfmtid,
|
|
DWORD grfMode,
|
|
BOOL fDelete,
|
|
HRESULT *phr)
|
|
{
|
|
HRESULT &hr = *phr;
|
|
CPropSetName psn(rfmtid);
|
|
IStorage *pstgParent;
|
|
IStorage *pstg = pprivstg->GetStorage();
|
|
USHORT createprop = 0L;
|
|
|
|
Initialize();
|
|
|
|
_grfAccess = 3 & grfMode;
|
|
_grfShare = 0xF0 & grfMode;
|
|
|
|
// Is this the special-case second-section property set?
|
|
_fUserDefinedProperties = ( rfmtid == FMTID_UserDefinedProperties ) ? TRUE : FALSE;
|
|
|
|
// check for any mode flags disallowed in Open.
|
|
if (grfMode & (STGM_CREATE | STGM_PRIORITY | STGM_CONVERT | STGM_TRANSACTED |
|
|
STGM_SIMPLE | STGM_DELETEONRELEASE))
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
return;
|
|
}
|
|
|
|
hr = pstg->OpenStream(psn.GetPropSetName(), NULL, _grfAccess | _grfShare,
|
|
0, &_pstmPropSet);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
pstgParent = pstg;
|
|
}
|
|
|
|
|
|
// Determine the CREATEPROP flags.
|
|
if( fDelete )
|
|
{
|
|
createprop = CREATEPROP_DELETE;
|
|
}
|
|
else
|
|
{
|
|
createprop = (S_OK == IsWriteable() ?
|
|
CREATEPROP_WRITE : CREATEPROP_READ);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// sets up _usCodePage
|
|
hr = InitializePropertyStream(
|
|
createprop,
|
|
&rfmtid,
|
|
NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::Initialize
|
|
//
|
|
// Synopsis: Initialize members to known values.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID CPropertyStorage::Initialize(VOID)
|
|
{
|
|
_ulSig = PROPERTYSTORAGE_SIG;
|
|
_cRefs = 1;
|
|
_pstgPropSet = NULL;
|
|
_pstmPropSet = NULL;
|
|
_dwOSVersion = PROPSETHDR_OSVERSION_UNKNOWN;
|
|
_np = NULL;
|
|
_ms = NULL;
|
|
_usCodePage = CP_WINUNICODE;
|
|
_grfFlags = 0;
|
|
_grfAccess = 0;
|
|
_grfShare = 0;
|
|
_fUserDefinedProperties = FALSE;
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::InitializePropertyStream.
|
|
//
|
|
// Synopsis: Initialize the storage-type specific members.
|
|
//
|
|
// Arguments: [Flags] -- Flags for RtlCreatePropertySet: CREATEPROP_*
|
|
// [pguid] -- FMTID, in for create only.
|
|
// [pclsid] -- Class id, in for create only.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Requires:
|
|
// _pstmPropSet -- The IStream of the main property set stream.
|
|
//
|
|
// Modifies: _ms (NTMAPPEDSTREAM)
|
|
//
|
|
// (assumed NULL on entry) will be NULL or valid on exit
|
|
//
|
|
// if _fNative, then _ms is CNtMappedStream*
|
|
// if !_fNative, then _ms is CMappedStream* of CExposedStream
|
|
//
|
|
// _np (NTPROP) aka CPropertySetStream
|
|
//
|
|
// (assumed NULL on entry) will be NULL or valid on exit
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::InitializePropertyStream(
|
|
USHORT Flags,
|
|
const GUID *pguid,
|
|
GUID const *pclsid)
|
|
{
|
|
HRESULT hr;
|
|
|
|
CExposedStream *pexpstm = (CExposedStream*)_pstmPropSet;
|
|
PROPASSERT(pexpstm->Validate() != STG_E_INVALIDHANDLE );
|
|
_ms = (CMappedStream*)pexpstm;
|
|
hr = S_OK;
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::InitializePropertyStream"
|
|
" - using CExposedDocfile as CMappedStream\n", this));
|
|
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlCreatePropertySet(
|
|
_ms,
|
|
Flags,
|
|
pguid,
|
|
pclsid,
|
|
(NTMEMORYALLOCATOR) & g_CoTaskAllocator,
|
|
GetUserDefaultLCID(),
|
|
&_dwOSVersion,
|
|
&_usCodePage,
|
|
&_np);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::InitializePropertyStream"
|
|
" - RtlCreatePropertySet Status=%08X\n", this, Status));
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (_usCodePage != CP_WINUNICODE)
|
|
_grfFlags |= PROPSETFLAG_ANSI; // for Stat
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::~CPropertyStorage
|
|
//
|
|
// Synopsis: Free up object resources.
|
|
//
|
|
// Notes: Cleans up even from partial construction.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CPropertyStorage::~CPropertyStorage()
|
|
{
|
|
_ulSig = PROPERTYSTORAGE_SIGDEL; // prevent someone else deleting it
|
|
|
|
if (_np != NULL)
|
|
{
|
|
// RtlFlushPropertySet(_np);
|
|
RtlClosePropertySet(_np);
|
|
}
|
|
|
|
if (_pstmPropSet != NULL)
|
|
_pstmPropSet->Release();
|
|
|
|
if (_pstgPropSet != NULL)
|
|
_pstgPropSet->Release();
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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 (IsEqualIID(riid,IID_IPropertyStorage) || IsEqualIID(riid,IID_IUnknown))
|
|
{
|
|
*ppvObject = (IPropertyStorage *)this;
|
|
CPropertyStorage::AddRef();
|
|
}
|
|
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::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.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::ReadMultiple(
|
|
ULONG cpspec,
|
|
const PROPSPEC rgpspec[],
|
|
PROPVARIANT rgpropvar[])
|
|
{
|
|
NTSTATUS Status;
|
|
HRESULT hr;
|
|
ULONG cpropFound;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReadable()))
|
|
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;
|
|
|
|
// -------------------
|
|
// Read the Properties
|
|
// -------------------
|
|
|
|
Status = RtlQueryProperties(
|
|
_np,
|
|
cpspec,
|
|
rgpspec,
|
|
NULL, // don't want PROPID's
|
|
rgpropvar,
|
|
&cpropFound);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (cpropFound == 0)
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
PropDbg((DEB_PROP_EXIT,
|
|
"CPropertyStorage(%08X)::ReadMultiple(cpspec=%d, rgpspec=%08X, "
|
|
"rgpropvar=%08X) returns %08X\n",
|
|
this, cpspec, rgpspec, rgpropvar, hr));
|
|
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.
|
|
// Errors --
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Notes:
|
|
// 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;
|
|
CStackPropIdArray spia;
|
|
|
|
if (S_OK != (hr = spia.Init(cpspec)))
|
|
return(hr);
|
|
|
|
Status = RtlSetProperties(_np, // property set context
|
|
cpspec, // property count
|
|
propidNameFirst, // first propid for new named props
|
|
rgpspec, // array of property specifiers
|
|
spia.GetBuf(), // buffer for array of propids
|
|
rgpropvar);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
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.
|
|
// Errors -- accordingly
|
|
//
|
|
// 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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
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;
|
|
|
|
// --------------------
|
|
// Write the Properties
|
|
// --------------------
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
PropDbg((DEB_PROP_EXIT,
|
|
"CPropertyStorage(%08X)::WriteMultiple(cpspec=%d, rgpspec=%08X, "
|
|
"rgpropvar=%08X, propidNameFirst=%d) returns %08X\n",
|
|
this, cpspec, rgpspec, rgpropvar, propidNameFirst, hr));
|
|
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.
|
|
// Errors --
|
|
//
|
|
// Notes: Checks that rgpropvar is not NULL, then calls
|
|
// _WriteMultiple.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::DeleteMultiple(
|
|
ULONG cpspec,
|
|
const PROPSPEC rgpspec[])
|
|
{
|
|
HRESULT hr;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if (0 == cpspec)
|
|
{
|
|
hr = S_OK;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPSPEC( cpspec, rgpspec )))
|
|
goto errRet;
|
|
|
|
// ---------------------
|
|
// Delete the Properties
|
|
// ---------------------
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
PropDbg((DEB_PROP_EXIT,
|
|
"CPropertyStorage(%08X)::DeleteMultiple(cpspec=%d, rgpspec=%08X) "
|
|
"returns %08X\n",
|
|
this, cpspec, rgpspec, hr));
|
|
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;
|
|
|
|
// --------
|
|
// Validate
|
|
// --------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReadable()))
|
|
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;
|
|
|
|
// --------------
|
|
// Read the Names
|
|
// --------------
|
|
|
|
Status = RtlQueryPropertyNames(_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:
|
|
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::ReadPropertyNames(cpropid=%d, rgpropid=%08X, "
|
|
"rglpwstrName=%08X) returns %08X\n",
|
|
this, cpropid, rgpropid, rglpwstrName, hr));
|
|
|
|
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;
|
|
|
|
Status = RtlSetPropertyNames(_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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
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;
|
|
|
|
// ---------------
|
|
// Write the Names
|
|
// ---------------
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::WritePropertyNames(cpropid=%d, rgpropid=%08X, "
|
|
"rglpwstrName=%08X) returns %08X\n",
|
|
this, cpropid, rgpropid, rglpwstrName, hr));
|
|
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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if( 0 == cpropid )
|
|
{
|
|
hr = S_OK;
|
|
goto errRet;
|
|
}
|
|
|
|
if (S_OK != (hr = ValidateRGPROPID( cpropid, rgpropid )))
|
|
goto errRet;
|
|
|
|
// ----------------
|
|
// Delete the Names
|
|
// ----------------
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::DeletePropertyNames(cpropid=%d, rgpropid=%08X) "
|
|
"returns %08X\n",
|
|
this, cpropid, rgpropid, hr));
|
|
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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
if (S_OK != (hr = VerifyCommitFlags(grfCommitFlags)))
|
|
goto errRet;
|
|
|
|
// --------------------------
|
|
// Commit the PropertyStorage
|
|
// --------------------------
|
|
|
|
Status = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::Commit(grfCommitFlags=%08X) "
|
|
"returns %08X\n",
|
|
this, grfCommitFlags, hr));
|
|
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;
|
|
|
|
hr = Validate();
|
|
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::Revert() "
|
|
"returns %08X\n",
|
|
this, hr));
|
|
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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
if (S_OK != (hr = IsReadable()))
|
|
return(hr);
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
return(hr);
|
|
|
|
// Validate the inputs
|
|
|
|
VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
|
|
|
|
// ----------------------
|
|
// Create the Enumeration
|
|
// ----------------------
|
|
|
|
*ppenum = NULL;
|
|
|
|
hr = STG_E_INSUFFICIENTMEMORY;
|
|
|
|
*ppenum = new CEnumSTATPROPSTG(_np, &hr);
|
|
if (FAILED(hr))
|
|
{
|
|
delete (CEnumSTATPROPSTG*) *ppenum;
|
|
*ppenum = NULL;
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
VDATEPTRIN_LABEL( pctime, FILETIME, errRet, hr );
|
|
VDATEPTRIN_LABEL( patime, FILETIME, errRet, hr );
|
|
VDATEPTRIN_LABEL( pmtime, FILETIME, errRet, hr );
|
|
|
|
// since we only support non-simple, this function does not
|
|
// do anything
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
PropDbg((DEB_PROP_EXIT,
|
|
"CPropertyStorage(%08X)::SetTimes("
|
|
"pctime=%08X, patime=%08X, pmtime=%08X) returns %08X\n",
|
|
this, pctime, patime, pmtime, hr));
|
|
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: Have clsid set into the property set stream.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
HRESULT CPropertyStorage::SetClass(REFCLSID clsid)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
DBGBUF(buf);
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsWriteable()))
|
|
goto errRet;
|
|
|
|
// Validate the inputs
|
|
|
|
GEN_VDATEREADPTRIN_LABEL(&clsid, CLSID, E_INVALIDARG, errRet, hr);
|
|
|
|
// -------------
|
|
// Set the CLSID
|
|
// -------------
|
|
|
|
// Set it in the property set header
|
|
|
|
Status = RtlSetPropertySetClassId(_np, &clsid);
|
|
if (!NT_SUCCESS(Status))
|
|
hr = DfpNtStatusToHResult(Status);
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( E_INVALIDARG != hr )
|
|
{
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::SetClass(clsid=%s) "
|
|
"returns %08X\n",
|
|
this, DbgFmtId(clsid, buf), hr));
|
|
}
|
|
else
|
|
{
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::SetClass(clsid@%08X) "
|
|
"returns %08X\n",
|
|
this, &clsid, hr));
|
|
}
|
|
|
|
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 * p)
|
|
{
|
|
HRESULT hr;
|
|
NTSTATUS Status;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReverted()))
|
|
goto errRet;
|
|
|
|
if (S_OK != (hr = IsReadable()))
|
|
goto errRet;
|
|
|
|
// Validate inputs
|
|
|
|
VDATEPTROUT_LABEL(p, STATPROPSETSTG, errRet, hr);
|
|
|
|
// ------------
|
|
// Get the Stat
|
|
// ------------
|
|
|
|
ZeroMemory(p, sizeof(*p));
|
|
|
|
// returns mtime, ansi flag, clsid, fmtid
|
|
Status = RtlQueryPropertySet(_np, p);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
STATSTG statstg;
|
|
|
|
hr = S_OK;
|
|
hr = _pstmPropSet->Stat(&statstg, STATFLAG_NONAME);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
p->mtime = statstg.mtime;
|
|
p->ctime = statstg.ctime;
|
|
p->atime = statstg.atime;
|
|
p->grfFlags = _grfFlags;
|
|
p->dwOSVersion = _dwOSVersion;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ZeroMemory(p, sizeof(*p));
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::Stat(STATPROPSETSTG *p = %08X) "
|
|
"returns %08X\n",
|
|
this, p, hr));
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStatArray::CStatArray
|
|
//
|
|
// Synopsis: Read in the enumeration using RtlEnumerateProperties
|
|
//
|
|
// Arguments: [np] -- the NTPROP to use
|
|
// [phr] -- S_OK on success, error otherwise.
|
|
//
|
|
// Notes: Retry getting number of properties and reading all of
|
|
// them into a caller-allocated buffer until it fits.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CStatArray::CStatArray(NTPROP np, HRESULT *phr)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ulKeyZero;
|
|
ULONG cpropAllocated;
|
|
|
|
_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;
|
|
|
|
delete [] _psps;
|
|
_psps = NULL;
|
|
|
|
Status = RtlEnumerateProperties(
|
|
np,
|
|
ENUMPROP_NONAMES,
|
|
&ulKeyZero,
|
|
&_cpropActual,
|
|
NULL, // aprs
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
cpropAllocated = _cpropActual + 1;
|
|
|
|
_psps = new STATPROPSTG [ cpropAllocated ];
|
|
if (_psps == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
ulKeyZero = 0;
|
|
Status = RtlEnumerateProperties(
|
|
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 (_psps != NULL)
|
|
{
|
|
CleanupSTATPROPSTG(_cpropActual, _psps);
|
|
}
|
|
delete [] _psps;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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 for object that has cursor over CStatArray
|
|
// and implements IEnumSTATPROPSTG, used by
|
|
// CPropertyStorage::Enum.
|
|
//
|
|
// Arguments: [np] -- the NTPROP to use
|
|
// [phr] -- where to put the HRESULT
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CEnumSTATPROPSTG::CEnumSTATPROPSTG(NTPROP np, HRESULT *phr)
|
|
{
|
|
_ulSig = ENUMSTATPROPSTG_SIG;
|
|
_cRefs = 1;
|
|
|
|
_psa = new CStatArray(np, phr);
|
|
|
|
_ipropNext = 0;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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, HRESULT *phr)
|
|
{
|
|
|
|
_ulSig = ENUMSTATPROPSTG_SIG;
|
|
_cRefs = 1;
|
|
|
|
_psa = other._psa;
|
|
_psa->AddRef();
|
|
|
|
_ipropNext = other._ipropNext;
|
|
|
|
*phr = S_OK;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CEnumSTATPROPSTG::~CEnumSTATPROPSTG
|
|
//
|
|
// Synopsis: Deallocated storage.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CEnumSTATPROPSTG::~CEnumSTATPROPSTG()
|
|
{
|
|
_ulSig = ENUMSTATPROPSTG_SIGDEL; // prevent another thread doing it - kinda
|
|
|
|
if (_psa != NULL)
|
|
_psa->Release();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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)
|
|
{
|
|
if (S_OK != Validate())
|
|
return(0);
|
|
|
|
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 = (LPOLESTR)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: CleanupSTATPROPSTG
|
|
//
|
|
// Synopsis: Free any elements in the passed array.
|
|
//
|
|
// Arguments: [celt] -- number of elements to examine.
|
|
// [psps] -- array of STATPROPSTG to examine.
|
|
//
|
|
// Notes: Zeros them out too.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CleanupSTATPROPSTG(ULONG celt, STATPROPSTG * psps)
|
|
{
|
|
while (celt)
|
|
{
|
|
CoTaskMemFree(psps->lpwstrName);
|
|
memset(psps, 0, sizeof(*psps));
|
|
celt--;
|
|
psps++;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
{
|
|
CleanupSTATPROPSTG(celt, rgelt);
|
|
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;
|
|
|
|
// ----------
|
|
// Validation
|
|
// ----------
|
|
|
|
// Validate 'this'
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
return(hr);
|
|
|
|
// Validate the input
|
|
|
|
VDATEPTROUT( ppenum, IEnumSTATPROPSTG* );
|
|
|
|
// --------------------
|
|
// Clone the enumerator
|
|
// --------------------
|
|
|
|
*ppenum = NULL;
|
|
|
|
hr = STG_E_INSUFFICIENTMEMORY;
|
|
|
|
*ppenum = new CEnumSTATPROPSTG(*this, &hr);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
delete (CEnumSTATPROPSTG*)*ppenum;
|
|
*ppenum = NULL;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|