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