|
|
//+-------------------------------------------------------------------------
//
// 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); }
|