windows-nt/Source/XPSP1/NT/com/ole32/stg/props/propstg.hxx

822 lines
21 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: propstg.hxx
//
// Contents: Class that directly implements IPropertyStorage
//
// Classes: CCoTaskAllocator
// CPropertyStorage
// CEnumSTATPROPSTG
//
// Functions:
//
//
//
// History: 1-Mar-95 BillMo Created.
// 25-Jan-96 MikeHill Added _fmtidSection.
// 22-May-96 MikeHill Added _dwOSVersion to CPropertyStorage.
// 06-Jun-96 MikeHill Added support for input validation.
// 18-Aug-96 MikeHill - Added CDocFilePropertyStorage.
// 07-Feb-97 Danl - Removed CDocFilePropertyStorage.
// 18-Aug-98 MikeHill Probe stream in IsWriteable.
//
// Notes:
//
//--------------------------------------------------------------------------
//+-------------------------------------------------------------------------
//
// Class: CCoTaskAllocator
//
// Purpose: Class used by PrQueryProperties to allocate memory.
//
//--------------------------------------------------------------------------
class CCoTaskAllocator : public PMemoryAllocator
{
public:
virtual void *Allocate(ULONG cbSize);
virtual void Free(void *pv);
};
extern const OLECHAR g_oszPropertyContentsStreamName[];
//+-------------------------------------------------------------------------
//
// Class: CPropertyStorage
//
// Purpose: Implements IPropertyStorage.
//
// Notes: This class uses the functionality provided by
// PrCreatePropertySet, PrSetProperties, PrQueryProperties etc
// to manipulate the property set stream.
//
//--------------------------------------------------------------------------
#define PROPERTYSTORAGE_SIG LONGSIG('P','R','P','S')
#define PROPERTYSTORAGE_SIGDEL LONGSIG('P','R','P','s')
#define PROPERTYSTORAGE_SIGZOMBIE LONGSIG('P','R','P','z')
#define ENUMSTATPROPSTG_SIG LONGSIG('E','P','S','S')
#define ENUMSTATPROPSTG_SIGDEL LONGSIG('E','P','S','s')
// Prototype for test function
WORD GetFormatVersion( IPropertyStorage *ppropstg );
class CPropertyStorage : public IPropertyStorage
#if DBG
, public IStorageTest
#endif
{
friend WORD GetFormatVersion(IPropertyStorage *ppropstg); // Used for test only
// ------------
// Constructors
// ------------
public:
// The destructor must be virtual so that derivative destructors
// will execute.
virtual ~CPropertyStorage();
CPropertyStorage(MAPPED_STREAM_OPTS fMSOpts)
{
_fInitCriticalSection = FALSE;
_fMSOpts = fMSOpts;
Initialize();
#ifndef _MAC
__try
{
InitializeCriticalSection( &_CriticalSection );
_fInitCriticalSection = TRUE;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
}
#endif
}
// --------
// IUnknown
// --------
public:
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// ----------------
// IPropertyStorage
// ----------------
public:
STDMETHOD(ReadMultiple)(
ULONG cpspec,
const PROPSPEC rgpspec[],
PROPVARIANT rgpropvar[]);
STDMETHOD(WriteMultiple)(
ULONG cpspec,
const PROPSPEC rgpspec[],
const PROPVARIANT rgpropvar[],
PROPID propidNameFirst);
STDMETHOD(DeleteMultiple)(
ULONG cpspec,
const PROPSPEC rgpspec[]);
STDMETHOD(ReadPropertyNames)(
ULONG cpropid,
const PROPID rgpropid[],
LPOLESTR rglpwstrName[]);
STDMETHOD(WritePropertyNames)(
ULONG cpropid,
const PROPID rgpropid[],
const LPOLESTR rglpwstrName[]);
STDMETHOD(DeletePropertyNames)(
ULONG cpropid,
const PROPID rgpropid[]);
STDMETHOD(Commit)(DWORD grfCommitFlags);
STDMETHOD(Revert)();
STDMETHOD(Enum)(IEnumSTATPROPSTG ** ppenum);
STDMETHOD(SetTimes)(
FILETIME const * pctime,
FILETIME const * patime,
FILETIME const * pmtime);
STDMETHOD(SetClass)(REFCLSID clsid);
STDMETHOD(Stat)(STATPROPSETSTG * pstatpsstg);
// ------------
// IStorageTest
// ------------
public:
#if DBG
STDMETHOD(UseNTFS4Streams)( BOOL fUseNTFS4Streams );
STDMETHOD(GetFormatVersion)(WORD *pw);
STDMETHOD(SimulateLowMemory)( BOOL fSimulate );
STDMETHOD(GetLockCount)();
STDMETHOD(IsDirty)();
#endif
// -----------------------------
// Exposed non-interface methods
// -----------------------------
public:
HRESULT Create(
IStream *stm,
REFFMTID rfmtid,
const CLSID *pclsid,
DWORD grfFlags,
DWORD grfMode );
HRESULT Create(
IStorage *pstg,
REFFMTID rfmtid,
const CLSID *pclsid,
DWORD grfFlags,
DWORD grfMode );
HRESULT Open(
IStorage *pstg,
REFFMTID rfmtid,
DWORD grfFlags,
DWORD grfMode );
HRESULT Open(
IStream *pstm,
REFFMTID rfmtid,
DWORD grfFlags,
DWORD grfMode,
BOOL fDelete );
// used by implementation helper classes
inline NTPROP GetNtPropSetHandle(void) { return _np; }
// ----------------
// Internal Methods
// ----------------
private:
void Initialize();
HRESULT InitializeOnCreateOrOpen(
DWORD grfFlags,
DWORD grfMode,
REFFMTID rfmtid,
BOOL fCreate );
VOID CleanupOpenedObjects(
PROPVARIANT avar[],
INDIRECTPROPERTY * pip,
ULONG cpspec,
ULONG iFailIndex);
enum EInitializePropertyStream
{
OPEN_PROPSTREAM,
CREATE_PROPSTREAM,
DELETE_PROPSTREAM
};
HRESULT InitializePropertyStream(
const GUID * pguid,
GUID const * pclsid,
EInitializePropertyStream CreateOpenDelete );
HRESULT _WriteMultiple(
ULONG cpspec,
const PROPSPEC rgpspec[],
const PROPVARIANT rgpropvar[],
PROPID propidNameFirst);
HRESULT _WritePropertyNames(
ULONG cpropid,
const PROPID rgpropid[],
const LPOLESTR rglpwstrName[]);
BOOL ProbeStreamToDetermineIfWriteable();
virtual HRESULT CreateMappedStream( );
virtual void DeleteMappedStream( );
VOID Lock();
VOID Unlock();
VOID AssertLocked();
inline HRESULT Validate();
inline HRESULT ValidateRef();
inline HRESULT ValidateRGPROPSPEC( ULONG cpspec, const PROPSPEC rgpropspec[] );
inline HRESULT ValidateRGPROPID( ULONG cpropid, const PROPID rgpropid[] );
HRESULT ValidateVTs( ULONG cprops, const PROPVARIANT rgpropvar[] );
enum EIsWriteable
{
PROBE_IF_NECESSARY,
DO_NOT_PROBE
};
inline BOOL IsNonSimple();
inline BOOL IsSimple();
inline BOOL IsWriteable( EIsWriteable ProbeEnum = PROBE_IF_NECESSARY );
inline BOOL IsReadable();
inline BOOL IsReverted();
inline DWORD GetChildCreateMode();
inline DWORD GetChildOpenMode();
// -------------
// Internal Data
// -------------
private:
ULONG _ulSig;
LONG _cRefs;
IStorage * _pstgPropSet;
IStream * _pstmPropSet;
NTPROP _np;
IMappedStream * _ms;
MAPPED_STREAM_OPTS _fMSOpts;
#ifndef _MAC
BOOL _fInitCriticalSection;
CRITICAL_SECTION _CriticalSection;
#endif
#if DBG
LONG _cLocks;
#endif
// We need to remember if the property set is the second section
// of the DocumentSummaryInformation property set used by Office. This
// is the only case where we support a multiple-section property set, and
// requires special handling in ::Revert().
BOOL _fUserDefinedProperties:1;
// If _grfMode is zero, maybe it's valid and really zero, or maybe
// we're dealing with a stream that doesn't return the grfMode in the
// Stat (such as is the case with CreateStreamFromHGlobal). So if we
// get a zero _grfMode, we probe the stream to determine if it's writeable.
// This flag ensures we only do it once.
BOOL _fExplicitelyProbedForWriteAccess:1;
USHORT _usCodePage; // Ansi Codepage or Mac Script
// (disambiguate with _dwOSVersion)
ULONG _dwOSVersion; // Shows the OS Kind, major/minor version
DWORD _grfFlags; // PROPSETFLAG_NONSIMPLE and PROPSETFLAG_ANSI
DWORD _grfMode; // STGM_ flags
// The following is a PMemoryAllocator for the Rtl property
// set routines. This instantiation was formerly a file-global
// in propstg.cxx. However, on the Mac, the object was not
// being instantiated.
CCoTaskAllocator _cCoTaskAllocator;
};
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::Validate
//
// Synopsis: S_OK if signature valid and not zombie.
//
// Notes: If the Revert calls fails due to low memory, then
// the object becomes a zombie.
//
//--------------------------------------------------------------------
inline HRESULT CPropertyStorage::Validate()
{
if(!_fInitCriticalSection)
return E_OUTOFMEMORY;
if (_ulSig == PROPERTYSTORAGE_SIG)
return S_OK;
else
if (_ulSig == PROPERTYSTORAGE_SIGZOMBIE)
return STG_E_INSUFFICIENTMEMORY;
else
return STG_E_INVALIDHANDLE;
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::ValidateRef
//
// Synopsis: S_OK if signature valid.
//
//--------------------------------------------------------------------
inline HRESULT CPropertyStorage::ValidateRef()
{
if (_ulSig == PROPERTYSTORAGE_SIG || _ulSig == PROPERTYSTORAGE_SIGZOMBIE)
return(S_OK);
else
return(STG_E_INVALIDHANDLE);
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::AssertLocked
//
// Synopsis: Asserts if _cLocks is <= zero.
//
//--------------------------------------------------------------------
inline VOID CPropertyStorage::AssertLocked()
{
DfpAssert( 0 < _cLocks );
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::ValidateRGPROPSPEC
//
// Synopsis: S_OK if PROPSPEC[] is valid
// E_INVALIDARG otherwise.
//
//--------------------------------------------------------------------
inline HRESULT CPropertyStorage::ValidateRGPROPSPEC( ULONG cpspec,
const PROPSPEC rgpropspec[] )
{
HRESULT hr = E_INVALIDARG;
// Validate the array itself.
VDATESIZEREADPTRIN_LABEL(rgpropspec, cpspec * sizeof(PROPSPEC), errRet, hr);
// Validate the elements of the array.
for( ; cpspec > 0; cpspec-- )
{
// Is this an LPWSTR?
if( PRSPEC_LPWSTR == rgpropspec[cpspec-1].ulKind )
{
// We better at least be able to read the first
// character.
VDATEREADPTRIN_LABEL(rgpropspec[cpspec-1].lpwstr, WCHAR, errRet, hr);
}
// Otherwise, this better be a PROPID.
else if( PRSPEC_PROPID != rgpropspec[cpspec-1].ulKind )
{
goto errRet;
}
}
hr = S_OK;
// ----
// Exit
// ----
errRet:
return( hr );
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::ValidateRGPROPID
//
// Synopsis: S_OK if RGPROPID[] is valid for Read.
// E_INVALIDARG otherwise.
//
//--------------------------------------------------------------------
inline HRESULT CPropertyStorage::ValidateRGPROPID( ULONG cpropid,
const PROPID rgpropid[] )
{
HRESULT hr;
VDATESIZEREADPTRIN_LABEL( rgpropid, cpropid * sizeof(PROPID), errRet, hr );
hr = S_OK;
errRet:
return( hr );
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::IsNonSimple
//
// Synopsis: true if non-simple
//
//--------------------------------------------------------------------
inline BOOL CPropertyStorage::IsNonSimple()
{
AssertLocked();
return (_grfFlags & PROPSETFLAG_NONSIMPLE) != 0;
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::IsSimple
//
// Synopsis: true if simple
//
//--------------------------------------------------------------------
inline BOOL CPropertyStorage::IsSimple()
{
AssertLocked();
return !IsNonSimple();
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::IsWriteable
//
// Synopsis: Returns TRUE if writeable, FALSE otherwise.
//
// If _grfMode is zero, we may actually have a writeable
// stream, but _grfMode wasn't returned in IStream::Stat.
// So unless explicitely told not to, we'll probe for
// write access. If explicitely told not to
// (by passing DO_NOT_PROBE), we'll assume that it's
// read-only.
//
//--------------------------------------------------------------------
inline BOOL CPropertyStorage::IsWriteable( EIsWriteable ProbeEnum )
{
AssertLocked();
if( ::GrfModeIsWriteable(_grfMode) )
return( TRUE );
if( 0 != _grfMode || _fExplicitelyProbedForWriteAccess )
// It's explicitely not writeable
return( FALSE );
else
{
// It's either STGM_READ|STGM_DENYNONE, or _grfMode
// wasn't set.
if( PROBE_IF_NECESSARY == ProbeEnum )
return( ProbeStreamToDetermineIfWriteable() );
else
return( FALSE ); // Play it safe and assume read-only.
}
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::IsReadable
//
// Synopsis: S_OK if readable otherwise STG_E_ACCESSDENIED
//
//--------------------------------------------------------------------
inline BOOL CPropertyStorage::IsReadable()
{
AssertLocked();
return( ::GrfModeIsReadable(_grfMode) );
}
//+-------------------------------------------------------------------
//
// Member: CPropertyStorage::IsReverted
//
// Synopsis: TRUE if reverted, FALSE otherwise.
//
//--------------------------------------------------------------------
inline BOOL CPropertyStorage::IsReverted()
{
IUnknown *punk = NULL;
HRESULT hr = S_OK;
BOOL fReverted = FALSE;
AssertLocked();
if( (NULL == _pstgPropSet) && (NULL == _pstmPropSet) )
return( TRUE );
hr = (IsNonSimple() ? (IUnknown*)_pstgPropSet : (IUnknown*)_pstmPropSet) ->
QueryInterface(IID_IUnknown, (void**)&punk);
// Note: On older Mac implementations, memory-based streams will
// return E_NOINTERFACE here, due to a bug in the QueryInterface
// implementation.
if( STG_E_REVERTED == hr )
fReverted = TRUE;
else
fReverted = FALSE;;
if( SUCCEEDED(hr) )
punk->Release();
return( fReverted );
}
inline DWORD
CPropertyStorage::GetChildCreateMode()
{
DWORD dwMode;
AssertLocked();
dwMode = _grfMode & ~(STGM_SHARE_MASK|STGM_TRANSACTED);
dwMode |= STGM_SHARE_EXCLUSIVE | STGM_CREATE;
return( dwMode );
}
inline DWORD
CPropertyStorage::GetChildOpenMode()
{
AssertLocked();
return( GetChildCreateMode() & ~STGM_CREATE );
}
//+-------------------------------------------------------------------------
//
// Class: IStatArray
//
//--------------------------------------------------------------------------
interface IStatArray : public IUnknown
{
public:
virtual NTSTATUS NextAt( ULONG ipropNext, STATPROPSTG *pspsDest, ULONG *pceltFetched )=0;
};
//+-------------------------------------------------------------------------
//
// Class: CStatArray
//
// Purpose: Class to allow sharing of enumeration STATPROPSTG state.
//
// Interface: CStatArray -- Enumerate entire NTPROP np.
// NextAt -- Perform an OLE-type Next operation starting at
// specified offset.
// AddRef -- for sharing of this by CEnumSTATPROPSTG
// Release -- ditto
//
// Notes: Each IEnumSTATPROPSTG instance has a reference to a
// CStatArray. When IEnumSTATPROPSTG::Clone is called, a
// new reference to the extant CStatArray is used: no copying.
//
// The CEnumSTATPROPSTG has a cursor into the CStatArray.
//
//--------------------------------------------------------------------------
class CStatArray : public IStatArray
{
public:
CStatArray( NTPROP np, HRESULT *phr );
private:
~CStatArray();
public:
STDMETHODIMP QueryInterface ( REFIID riid, void **ppvObject);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
public:
NTSTATUS NextAt(ULONG ipropNext, STATPROPSTG *pspsDest, ULONG *pceltFetched);
private:
LONG _cRefs;
STATPROPSTG * _psps;
ULONG _cpropActual;
};
//+-------------------------------------------------------------------------
//
// Class: CEnumSTATPROPSTG
//
// Purpose: Implement IEnumSTATPROPSTG
//
// Notes: Just holds a reference to a CStatArray that contains
// a static copy of the enumeration when the original
// Enum call was made. This object contains the cursor
// into the CStatArray.
//
//--------------------------------------------------------------------------
NTSTATUS CopySTATPROPSTG( ULONG celt, STATPROPSTG * pspsDest, const STATPROPSTG * pspsSrc);
class CEnumSTATPROPSTG : public IEnumSTATPROPSTG
{
// ------------
// Construction
// ------------
public:
CEnumSTATPROPSTG( IStatArray *psa ): _ulSig(ENUMSTATPROPSTG_SIG),
_ipropNext(0),
_psa(psa),
_cRefs(1)
{
_psa->AddRef();
}
CEnumSTATPROPSTG(const CEnumSTATPROPSTG &other );
// -----------
// Destruction
// -----------
private:
~CEnumSTATPROPSTG();
// ----------------
// IUnknown Methods
// ----------------
public:
STDMETHOD(QueryInterface)( REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// -------------
// IEnum Methods
// -------------
public:
STDMETHOD(Skip)(ULONG celt);
STDMETHOD(Reset)();
STDMETHOD(Clone)(IEnumSTATPROPSTG ** ppenum);
// We don't need RemoteNext.
STDMETHOD(Next)(ULONG celt, STATPROPSTG * rgelt, ULONG * pceltFetched);
// ----------------
// Internal Methods
// ----------------
private:
HRESULT Validate();
// --------------
// Internal State
// --------------
private:
ULONG _ulSig;
LONG _cRefs;
IStatArray * _psa;
ULONG _ipropNext;
};
//+-------------------------------------------------------------------
//
// Member: CEnumSTATPROPSTG::Validate
//
// Synopsis: S_OK if signature is valid, otherwise error
//
//--------------------------------------------------------------------
inline HRESULT CEnumSTATPROPSTG::Validate()
{
return _ulSig == ENUMSTATPROPSTG_SIG ? S_OK : STG_E_INVALIDHANDLE;
}
//+-------------------------------------------------------------------
//
// Member: CheckFlagsOnCreateOrOpen
//
// Synopsis:
//
// Notes:
//
//
//--------------------------------------------------------------------
inline HRESULT CheckFlagsOnCreateOrOpen(
BOOL fCreate,
DWORD grfMode
)
{
// Check for any mode disallowed flags
if (grfMode & ( (fCreate ? 0 : STGM_CREATE)
|
STGM_PRIORITY | STGM_CONVERT
|
STGM_DELETEONRELEASE ))
{
return (STG_E_INVALIDFLAG);
}
// Ensure that we'll have read/write access to any storage/stream we create.
// If grfMode is zero, assume that it's uninitialized and that we'll have
// read/write access. If we don't, the client will still get an error
// at some point, just not in the open path.
if( fCreate
&&
(grfMode & STGM_READWRITE) != STGM_READWRITE
&&
0 != grfMode )
{
return (STG_E_INVALIDFLAG);
}
return(S_OK);
}