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