3662 lines
99 KiB
C++
3662 lines
99 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#include <tstr.h>
|
|
|
|
#ifdef _MAC_NODOC
|
|
ASSERTDATA // File-specific data for FnAssert
|
|
#endif
|
|
|
|
#ifndef _MAC // No InfoLevel debug functionality on Mac.
|
|
DECLARE_INFOLEVEL(prop)
|
|
#endif
|
|
|
|
|
|
//
|
|
// On NT, we (OLE32) must give the Rtl propset routines (in NTDLL)
|
|
// function pointers for Win32/OleAut functions.
|
|
//
|
|
|
|
#ifdef WINNT
|
|
VOID
|
|
SetRtlUnicodeCallouts(VOID)
|
|
{
|
|
static UNICODECALLOUTS UnicodeCallouts = { WIN32_UNICODECALLOUTS };
|
|
static BOOLEAN fSetUnicodeCallouts = FALSE;
|
|
|
|
if (!fSetUnicodeCallouts)
|
|
{
|
|
RtlSetUnicodeCallouts(&UnicodeCallouts);
|
|
fSetUnicodeCallouts = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// The name of the main data stream in non-simple property sets.
|
|
//
|
|
|
|
OLECHAR const ocsContents[] = {OLESTR("CONTENTS")};
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CCoTaskAllocator::Allocate, Free.
|
|
//
|
|
// Synopsis: A PMemoryAllocator used by the Rtl*
|
|
// 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);
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CDocFilePropertyStorage::Create
|
|
//
|
|
// Synopsis: This method creates an IPropertyStorage on a given
|
|
// IPrivateStorage, which is known to be a
|
|
// CExposedDocFile. Unlike the to CPropertyStorage::Create
|
|
// methods, this method is used for both simple and non-
|
|
// simple property sets.
|
|
//
|
|
// Arguments: [IPrivateStorage*] pprivstg
|
|
// The CExposedDocFile.
|
|
// [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
|
|
// [HRESULT*]
|
|
// The return code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: Since this routine has access to the DocFile
|
|
// (via the IPrivateStorage), it can lock both the
|
|
// IStorage object and the IPropertyStorage object
|
|
// (using the DocFile's tree mutex).
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CDocFilePropertyStorage::Create(
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
IStorage *pstg = pprivstg->GetStorage();
|
|
|
|
//
|
|
// here we take the tree mutex of the docfile
|
|
// this can be reacquired in ->CreateStorage too so
|
|
// the lock must be reentrant for the thread
|
|
//
|
|
pprivstg->Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
// Initialize the CPropertyStorage data.
|
|
|
|
hr = InitializeOnCreateOrOpen( grfFlags, grfMode, rfmtid,
|
|
TRUE ); // => Create
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Ignore the PROPSETFLAG_UNBUFFERED flag. We can do this because
|
|
// a buffered and unbuffered implementation of *docfile* IPropertyStorage
|
|
// are functionally equivalent. That is, there is no visible difference
|
|
// to the client, because we flush the buffer on Commits and CopyTos,
|
|
// and because we open SHARE_EXCLUSIVE.
|
|
|
|
_grfFlags = _grfFlags & ~PROPSETFLAG_UNBUFFERED;
|
|
|
|
|
|
// BUGBUG: BillMo, Raid# 13235 -- if opening in shared mode can get uninitialized
|
|
// propset (OFS only).
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
int i=0;
|
|
|
|
while (i<=1)
|
|
{
|
|
hr = pstg->CreateStorage(psn.GetPropSetName(), grfMode, 0, 0, &_pstgPropSet);
|
|
if (FAILED(hr))
|
|
{
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::CPropertyStorage"
|
|
" - CreateStorage(%ls) attempt %d, hr=%08X\n", this, psn.GetPropSetName(), i+1, hr));
|
|
}
|
|
if (hr == S_OK)
|
|
{
|
|
fCreated = TRUE;
|
|
|
|
if (pclsid != NULL)
|
|
{
|
|
hr = _pstgPropSet->SetClass(*pclsid);
|
|
if (FAILED(hr))
|
|
{
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::CPropertyStorage"
|
|
" - SetClass(), hr=%08X\n", this, hr));
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
DWORD grfMode2 = STGM_CREATE | _grfAccess | _grfShare;// BUGBUG: don't need this on docfile.
|
|
|
|
hr = _pstgPropSet->CreateStream(ocsContents, grfMode2, 0, 0, &_pstmPropSet);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGBUF(buf);
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::CPropertyStorage"
|
|
" - CreateStream(contents), %s, hr=%08X\n",
|
|
this, DbgMode(grfMode2, buf), hr));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
if (hr != STG_E_FILEALREADYEXISTS)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
if (i == 0 && (grfMode & STGM_CREATE) == STGM_CREATE)
|
|
{
|
|
// BUGBUG: check whether this happens if we're overwriting a stream
|
|
pstg->DestroyElement(psn.GetPropSetName());
|
|
}
|
|
i++;
|
|
}
|
|
} // if (IsNonSimple())
|
|
|
|
else // This is a simple property set.
|
|
{
|
|
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((USHORT) CREATEPROP_CREATE |
|
|
(IsNonSimple() ? CREATEPROP_NONSIMPLE : 0),
|
|
&rfmtid,
|
|
pclsid);
|
|
|
|
if (hr == S_OK && (grfMode & STGM_TRANSACTED))
|
|
{
|
|
// make sure the contents stream is actually published if
|
|
// it was created in transacted mode.
|
|
hr = CPropertyStorage::Commit(STGC_DEFAULT);
|
|
if (FAILED(hr))
|
|
{
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::CPropertyStorage"
|
|
" - Commit(STGC_DEFAULT) hr=%08X\n", this, hr));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
if (hr != S_OK && fCreated)
|
|
{
|
|
//
|
|
// if we fail after creating the property set in storage, cleanup.
|
|
//
|
|
pstg->DestroyElement(psn.GetPropSetName());
|
|
// BUGBUG: review: this will revert them: they will close in destructor
|
|
}
|
|
|
|
// If we took the lock, release it.
|
|
if( fLocked )
|
|
pprivstg->Unlock();
|
|
|
|
} // CDocFilePropertyStorage::Create
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
|
|
// Calculate the STGM flags to use for the Open. Create & Convert
|
|
// don't have meaning for the Open, and Transacted isn't supported
|
|
// by IPropertyStorage
|
|
|
|
grfOpenMode = grfMode & ~(STGM_CREATE | STGM_CONVERT | STGM_TRANSACTED);
|
|
|
|
*pfCreated = FALSE;
|
|
|
|
// Try an Open
|
|
|
|
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: CDocFilePropertyStorage::Open
|
|
//
|
|
// Synopsis: This method opens an IPropertyStorage on a
|
|
// CExposedDocFile (using its IPrivateStorage
|
|
// interface).
|
|
//
|
|
// Arguments: [IPrivateStorage*] pprivstg
|
|
// The CExposedDocFile.
|
|
// [REFFMTID] rfmtid
|
|
// The ID of the property set.
|
|
// [DWORD] grfMode
|
|
// From the STGM_* enumeration
|
|
// [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.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CDocFilePropertyStorage::Open(
|
|
IPrivateStorage * pprivstg,
|
|
REFFMTID rfmtid,
|
|
DWORD grfMode,
|
|
BOOL fDelete,
|
|
HRESULT * phr)
|
|
{
|
|
HRESULT & hr = *phr;
|
|
USHORT createprop = 0L;
|
|
|
|
// String-ize the FMTID GUID to get the Stream/Storage name.
|
|
CPropSetName psn(rfmtid);
|
|
|
|
// Get the IStorage* from the IPrivateStorage.
|
|
IStorage *pstgParent;
|
|
IStorage *pstg = pprivstg->GetStorage();
|
|
|
|
// Get exclusive access to the Storage (using the DocFile
|
|
// tree mutex). We do an unconditional unlock in the Exit, so
|
|
// we must be sure to reach this statement before any gotos.
|
|
|
|
pprivstg->Lock(INFINITE);
|
|
|
|
// Initialize the CPropertyStorage members. We pass "Default"
|
|
// as the grfFlags value, because we're really going to infer its
|
|
// value from the property set.
|
|
|
|
hr = InitializeOnCreateOrOpen( PROPSETFLAG_DEFAULT, grfMode, rfmtid,
|
|
FALSE ); // => Open
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// Assume the property set is simple and attempt to open it.
|
|
// Mask out the STGM_TRANSACTED bit because we don't support it.
|
|
|
|
hr = pstg->OpenStream(psn.GetPropSetName(), NULL,
|
|
(_grfAccess | _grfShare) & ~STGM_TRANSACTED,
|
|
0, &_pstmPropSet);
|
|
|
|
// If we got a not-found error, then it might be a non-simple
|
|
// property set.
|
|
if (hr == STG_E_FILENOTFOUND)
|
|
{
|
|
hr = pstg->OpenStorage(psn.GetPropSetName(), NULL, grfMode, NULL, 0, &_pstgPropSet);
|
|
if (hr == S_OK)
|
|
{
|
|
// We've opened the IStorage with the property set.
|
|
// Now open the CONTENTS Stream.
|
|
// Mask out the STGM_TRANSACTED bit because we don't support it.
|
|
|
|
_grfFlags |= PROPSETFLAG_NONSIMPLE;
|
|
pstgParent = _pstgPropSet;
|
|
hr = _pstgPropSet->OpenStream(ocsContents, NULL,
|
|
(_grfAccess | _grfShare) & ~STGM_TRANSACTED,
|
|
0, &_pstmPropSet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pstgParent = pstg;
|
|
}
|
|
|
|
|
|
// Determine the CREATEPROP flags.
|
|
|
|
if( fDelete )
|
|
{
|
|
// Only simple Sections may be deleted (really, only the
|
|
// second section of the DocumentSummaryInformation property
|
|
// set may be deleted in this way).
|
|
DfpAssert( !IsNonSimple() );
|
|
|
|
createprop = CREATEPROP_DELETE;
|
|
}
|
|
else
|
|
{
|
|
createprop = (S_OK == IsWriteable() ? CREATEPROP_WRITE : CREATEPROP_READ)
|
|
|
|
|
(IsNonSimple() ? CREATEPROP_NONSIMPLE : 0);
|
|
}
|
|
|
|
// Initialize the property set Stream.
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// sets up _usCodePage
|
|
hr = InitializePropertyStream(
|
|
createprop,
|
|
&rfmtid,
|
|
NULL);
|
|
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
pprivstg->Unlock();
|
|
|
|
} // CDocFilePropertyStorage::Open()
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
|
|
#ifndef _MAC
|
|
InitializeCriticalSection( &_CriticalSection );
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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: _fNative TRUE if native file system
|
|
// FALSE if docfile
|
|
//
|
|
// _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 = S_OK;
|
|
|
|
#ifdef WINNT
|
|
SetRtlUnicodeCallouts();
|
|
#endif
|
|
|
|
hr = CreateMappedStream();
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
NTSTATUS Status;
|
|
|
|
DfpAssert( NULL == _np );
|
|
|
|
Status = RtlCreatePropertySet(
|
|
_ms,
|
|
Flags,
|
|
pguid,
|
|
pclsid,
|
|
(NTMEMORYALLOCATOR) &_cCoTaskAllocator,
|
|
GetUserDefaultLCID(),
|
|
&_dwOSVersion,
|
|
&_usCodePage,
|
|
&_np);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::InitializePropertyStream"
|
|
" - RtlCreatePropertySet Status=%08X\n", this, Status));
|
|
hr = DfpNtStatusToHResult(Status);
|
|
goto Exit;
|
|
}
|
|
|
|
if (_usCodePage != CP_WINUNICODE)
|
|
_grfFlags |= PROPSETFLAG_ANSI; // for Stat
|
|
|
|
Exit:
|
|
|
|
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
|
|
|
|
// Close the property set.
|
|
|
|
if (_np != NULL)
|
|
{
|
|
RtlClosePropertySet(_np);
|
|
}
|
|
|
|
// Free the mapped stream.
|
|
|
|
if (_ms != NULL)
|
|
{
|
|
delete (CCFMappedStream*) _ms;
|
|
}
|
|
|
|
// Free the Stream and/or Storage with the serialized data.
|
|
|
|
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();
|
|
}
|
|
#ifdef _CAIRO_
|
|
else
|
|
if (IsEqualIID(riid, IID_IAccessControl))
|
|
{
|
|
if (_pstmPropSet && S_OK==
|
|
_pstmPropSet->QueryInterface(IID_IAccessControl,(void**)&_pIAC))
|
|
{
|
|
*ppvObject = (IAccessControl *)this;
|
|
CPropertyStorage::AddRef();
|
|
_pIAC->Release();
|
|
}
|
|
else if (_pstgPropSet && S_OK==
|
|
_pstgPropSet->QueryInterface(IID_IAccessControl,(void**)&_pIAC))
|
|
{
|
|
// here we depend on _pIAC being the same for multiple QIs
|
|
// in the multi-thread case.
|
|
*ppvObject = (IAccessControl *)this;
|
|
CPropertyStorage::AddRef();
|
|
_pIAC->Release();
|
|
}
|
|
else
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
#endif
|
|
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)
|
|
{
|
|
ULONG iStgProp;
|
|
ULONG iiScan;
|
|
|
|
// 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: BUGBUG: SPEC: Returning the same IStream* for the same
|
|
// VT queried multiple times.
|
|
//
|
|
// RtlQueryProperties 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;
|
|
|
|
// ----------
|
|
// 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
|
|
// -------------------
|
|
|
|
Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
Status = RtlQueryProperties(
|
|
_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
|
|
|
|
ULONG iiScan;
|
|
ULONG iStgProp;
|
|
|
|
for (iiScan = 0;
|
|
hr == S_OK && (iStgProp = pip[iiScan].Index) != MAX_ULONG;
|
|
iiScan++ )
|
|
{
|
|
PROPVARIANT *pPropVar = rgpropvar + iStgProp;
|
|
|
|
if (IsNonSimple() && pPropVar->pwszVal[0] != L'\0')
|
|
{
|
|
VOID *pStreamOrStorage;
|
|
|
|
switch (pPropVar->vt)
|
|
{
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
|
|
// Mask out the STGM_TRANSACTED bit because we don't
|
|
// support it.
|
|
|
|
hr = _pstgPropSet->OpenStream((LPOLESTR) pPropVar->pwszVal,
|
|
NULL,
|
|
(_grfAccess | _grfShare) & ~STGM_TRANSACTED,
|
|
0,
|
|
(IStream**)&pStreamOrStorage);
|
|
break;
|
|
case VT_STORAGE:
|
|
case VT_STORED_OBJECT:
|
|
hr = _pstgPropSet->OpenStorage((LPOLESTR) pPropVar->pwszVal,
|
|
NULL,
|
|
_grfAccess | _grfShare,
|
|
NULL,
|
|
0,
|
|
(IStorage**)&pStreamOrStorage);
|
|
break;
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
void **ppv = (void**) &(pPropVar->pStorage);
|
|
CoTaskMemFree(*ppv);
|
|
*ppv = 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);
|
|
}
|
|
}
|
|
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.
|
|
//
|
|
CoTaskMemFree(pPropVar->pStream);
|
|
pPropVar->pStream = NULL;
|
|
pPropVar->vt = VT_EMPTY;
|
|
cpropFound --;
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (cpspec == 1)
|
|
break;
|
|
}
|
|
|
|
if (cpspec != 1 && pip != NULL)
|
|
PropFreeHeap(RtlProcessHeap(), 0, pip);
|
|
}
|
|
|
|
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();
|
|
|
|
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.
|
|
// S_FALSE -- all simple properties written, but non-simple
|
|
// types (VT_STREAM etc) were ignored.
|
|
// Errors --
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Notes: RtlSetProperties 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;
|
|
CStackPropIdArray spia;
|
|
INDIRECTPROPERTY * pip;
|
|
INDIRECTPROPERTY ip;
|
|
|
|
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
|
|
cpspec == 1 ? (INDIRECTPROPERTY**)&ip : &pip,
|
|
// array for indirect prop indexes
|
|
// indexes of entries that are
|
|
// now non-simple, or are simple
|
|
// but were non-simple.
|
|
rgpropvar);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (cpspec == 1)
|
|
{
|
|
if (ip.Index != MAX_ULONG)
|
|
pip = &ip;
|
|
else
|
|
pip = NULL;
|
|
}
|
|
|
|
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++ )
|
|
{
|
|
|
|
// BUGBUG: pwszName was NULL when writing VT_STREAM
|
|
OLECHAR oszStdPropName[sizeof("prop")+10+1];
|
|
OLECHAR *poszPropName = (LPOLESTR) pip[iiScan].poszName;
|
|
const PROPVARIANT *pPropVar = rgpropvar + iStgProp;
|
|
|
|
if (poszPropName == NULL)
|
|
{
|
|
DfpAssert(iStgProp >= 0 && iStgProp < cpspec);
|
|
PROPGENPROPERTYNAME (oszStdPropName, spia.GetBuf()[iStgProp]);
|
|
poszPropName = oszStdPropName;
|
|
}
|
|
|
|
switch (rgpropvar == NULL ? VT_ILLEGAL : pPropVar->vt)
|
|
{
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
{
|
|
IStream *pstm;
|
|
int i=0;
|
|
|
|
while (i<=1)
|
|
{
|
|
hr = _pstgPropSet->CreateStream(poszPropName,
|
|
GetCreationMode(),
|
|
0,
|
|
0,
|
|
&pstm);
|
|
if (hr == S_OK)
|
|
{
|
|
// BUGBUG: spec bug: does this destroy the seek position on
|
|
// source ? does it read from current position or
|
|
// from the beginning of the stream.
|
|
|
|
if (pPropVar->pStream != NULL)
|
|
{
|
|
ULARGE_INTEGER uli;
|
|
memset(&uli, -1, sizeof(uli));
|
|
hr = pPropVar->pStream->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,
|
|
GetCreationMode(),
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
PropFreeHeap(RtlProcessHeap(), 0, pip[iiScan].poszName);
|
|
|
|
if (cpspec == 1)
|
|
break;
|
|
}
|
|
|
|
if (cpspec != 1 && pip != NULL)
|
|
PropFreeHeap(RtlProcessHeap(), 0, pip);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No VT_STREAM etc was requested to be written.
|
|
// and no simple property overwrote a non-simple one.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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.
|
|
// 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;
|
|
|
|
// ----------
|
|
// 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
|
|
// --------------------
|
|
|
|
Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
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 = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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.
|
|
// 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;
|
|
|
|
// ----------
|
|
// 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
|
|
// ---------------------
|
|
|
|
Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
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 = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// --------
|
|
// 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
|
|
// --------------
|
|
|
|
Lock( INFINITE );
|
|
fLocked = TRUE;
|
|
|
|
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:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// ----------
|
|
// 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
|
|
// ---------------
|
|
|
|
Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
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 = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// ----------
|
|
// 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
|
|
// ----------------
|
|
|
|
Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
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 = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// ----------
|
|
// 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
|
|
// --------------------------
|
|
|
|
Lock( INFINITE );
|
|
fLocked = TRUE;
|
|
|
|
Status = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
if (hr == S_OK)
|
|
hr = _pstgPropSet->Commit(grfCommitFlags);
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
|
|
if (S_OK != (hr = Validate()))
|
|
goto errRet;
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
Lock(INFINITE);
|
|
fLocked = TRUE;
|
|
|
|
hr = _pstgPropSet->Revert(); // BUGBUG: dirty flags
|
|
if (hr == S_OK)
|
|
{
|
|
RtlClosePropertySet(_np);
|
|
_np = NULL;
|
|
|
|
#ifdef _CAIRO_
|
|
if (_fNative) // BUGBUG: put this specific code in IPrivateStorage
|
|
{
|
|
if (_ms != NULL)
|
|
{
|
|
RtlCloseMappedStream(_ms);
|
|
}
|
|
}
|
|
#endif
|
|
_pstmPropSet->Release();
|
|
_pstmPropSet = NULL;
|
|
_ms = NULL;
|
|
|
|
// BUGBUG: if one of these fails then we are in deep trouble.
|
|
// Mask out the STGM_TRANSACTED bit because we don't support it.
|
|
|
|
hr = _pstgPropSet->OpenStream(ocsContents, NULL,
|
|
(_grfAccess | _grfShare) & ~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(
|
|
(S_OK == IsWriteable() ? CREATEPROP_WRITE : CREATEPROP_READ) |
|
|
CREATEPROP_NONSIMPLE,
|
|
_fUserDefinedProperties ? &FMTID_UserDefinedProperties : NULL,
|
|
NULL);
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
_ulSig = PROPERTYSTORAGE_SIGZOMBIE;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
hr = S_OK;
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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 );
|
|
|
|
// -------------
|
|
// Set the Times
|
|
// -------------
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
hr = _pstgPropSet->SetElementTimes(
|
|
NULL,
|
|
pctime,
|
|
patime,
|
|
pmtime);
|
|
}
|
|
if( FAILED(hr) ) goto errRet;
|
|
|
|
// ----
|
|
// 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: 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);
|
|
|
|
// ----------
|
|
// 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
|
|
// -------------
|
|
|
|
Lock( INFINITE );
|
|
fLocked = TRUE;
|
|
|
|
// Set it in the property set header
|
|
|
|
Status = RtlSetPropertySetClassId(_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 = RtlFlushPropertySet(_np);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
hr = DfpNtStatusToHResult(Status);
|
|
}
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
errRet:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
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;
|
|
BOOL fLocked = FALSE;
|
|
|
|
// ----------
|
|
// 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
|
|
// ------------
|
|
|
|
Lock( INFINITE );
|
|
fLocked = TRUE;
|
|
|
|
ZeroMemory(p, sizeof(*p));
|
|
|
|
// returns mtime, ansi flag, clsid, fmtid
|
|
Status = RtlQueryPropertySet(_np, p);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
STATSTG statstg;
|
|
|
|
hr = S_OK;
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
hr = _pstgPropSet->Stat(&statstg, STATFLAG_NONAME);
|
|
}
|
|
else
|
|
{
|
|
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:
|
|
|
|
if( fLocked )
|
|
Unlock();
|
|
|
|
PropDbg((DEB_PROP_EXIT, "CPropertyStorage(%08X)::Stat(STATPROPSETSTG *p = %08X) "
|
|
"returns %08X\n",
|
|
this, p, hr));
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CDocFilePropertyStorage::Lock/Unlock
|
|
//
|
|
// Synopsis: Wait for acquisition of DocFile tree mutex.
|
|
//
|
|
// Inputs: [DWORD] dwTimeout (for Lock method)
|
|
// Timeout delay.
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CDocFilePropertyStorage::Lock(DWORD dwTimeout)
|
|
{
|
|
#ifndef IPROPERTY_DLL
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
((CExposedDocFile*)_pstgPropSet)->Lock(dwTimeout);
|
|
}
|
|
else
|
|
{
|
|
((CExposedStream*)_pstmPropSet)->Lock(S_OK != IsWriteable());
|
|
}
|
|
|
|
#else
|
|
DfpAssert( !"DocFile used in IProperty DLL" );
|
|
#endif
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CDocFilePropertyStorage::Unlock
|
|
//
|
|
// Synopsis: Release tree mutex.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
VOID CDocFilePropertyStorage::Unlock()
|
|
{
|
|
#ifndef IPROPERTY_DLL
|
|
|
|
if (IsNonSimple())
|
|
{
|
|
((CExposedDocFile*)_pstgPropSet)->Unlock();
|
|
}
|
|
else
|
|
{
|
|
((CExposedStream*)_pstmPropSet)->Unlock();
|
|
}
|
|
|
|
#else
|
|
DfpAssert( !"DocFile used in IProperty DLL" );
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: Lock & Unlock
|
|
//
|
|
// Synopsis: This methods take and release the CPropertyStorage's
|
|
// critical section.
|
|
//
|
|
// Inputs: [DWORD] dwTimeout (for Lock)
|
|
// Must be INFINITE
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// Notes: These methods are overridden by the CDocFilePropertyStorage
|
|
// derivation, and do nothing when built for the Macintosh
|
|
// (since the Mac has cooperative multi-threading, there is
|
|
// no concurrency problem).
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CPropertyStorage::Lock(DWORD dwTimeout)
|
|
{
|
|
DfpAssert( INFINITE == dwTimeout );
|
|
#ifndef _MAC
|
|
EnterCriticalSection( &_CriticalSection );
|
|
#endif
|
|
|
|
}
|
|
|
|
VOID
|
|
CPropertyStorage::Unlock()
|
|
{
|
|
#ifndef _MAC
|
|
LeaveCriticalSection( &_CriticalSection );
|
|
#endif
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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, _grfAccess, _grfShare, _fUserDefinedProperties,
|
|
// and g_ReservedMemory.
|
|
//
|
|
//+-----------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
CPropertyStorage::InitializeOnCreateOrOpen(
|
|
DWORD grfFlags,
|
|
DWORD grfMode,
|
|
REFFMTID rfmtid,
|
|
BOOL fCreate )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Validate that grfFlags is within the enumeration.
|
|
if (grfFlags & ~(PROPSETFLAG_ANSI | PROPSETFLAG_NONSIMPLE | PROPSETFLAG_UNBUFFERED))
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
goto Exit;
|
|
}
|
|
|
|
// Check for any mode disallowed flags
|
|
if (grfMode & ( (fCreate ? 0 : STGM_CREATE)
|
|
|
|
|
STGM_PRIORITY | STGM_CONVERT
|
|
|
|
|
STGM_SIMPLE | STGM_DELETEONRELEASE ))
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
goto Exit;
|
|
}
|
|
|
|
// Ensure that we'll have read/write access to any storage/stream we create.
|
|
if( fCreate
|
|
&&
|
|
(grfMode & STGM_READWRITE) != STGM_READWRITE )
|
|
{
|
|
hr = STG_E_INVALIDFLAG;
|
|
goto Exit;
|
|
}
|
|
|
|
// Store the grfFlags & grfMode.
|
|
_grfFlags = grfFlags;
|
|
_grfAccess = 3 & grfMode;
|
|
_grfShare = 0xF0 & grfMode;
|
|
|
|
// Is this the special-case second-section property set?
|
|
_fUserDefinedProperties = ( rfmtid == FMTID_UserDefinedProperties ) ? TRUE : FALSE;
|
|
|
|
if (fCreate
|
|
&&
|
|
(_grfFlags & PROPSETFLAG_ANSI) )
|
|
{
|
|
_usCodePage = 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
|
|
// [HRESULT*]
|
|
// The return code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CPropertyStorage::Create(
|
|
IStream *pstm,
|
|
REFFMTID rfmtid,
|
|
const CLSID *pclsid,
|
|
DWORD grfFlags,
|
|
HRESULT *phr)
|
|
{
|
|
HRESULT & hr = *phr;
|
|
BOOL fCreated = FALSE;
|
|
STATSTG statstg;
|
|
|
|
// Save and addref the Stream.
|
|
|
|
_pstmPropSet = pstm;
|
|
_pstmPropSet->AddRef();
|
|
|
|
// Initialize this object. We'll assume that the STGM_READWRITE bit
|
|
// is set, even if the statstg.grfMode doesn't have it set
|
|
// (this bit doesn't get set by the memory-based IStream::Stat implementation).
|
|
|
|
hr = _pstmPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = InitializeOnCreateOrOpen( grfFlags, statstg.grfMode | STGM_READWRITE, rfmtid,
|
|
TRUE ); // => Create
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
DfpAssert( !IsNonSimple() );
|
|
|
|
// Initialize the Stream.
|
|
|
|
hr = InitializePropertyStream(CREATEPROP_CREATE |
|
|
0,
|
|
&rfmtid,
|
|
pclsid);
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// If buffering is not desired, flush the property storage
|
|
// to the underlying Stream.
|
|
|
|
if( _grfFlags & PROPSETFLAG_UNBUFFERED )
|
|
{
|
|
NTSTATUS Status = RtlFlushPropertySet(_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));
|
|
}
|
|
|
|
return;
|
|
|
|
} // 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.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CPropertyStorage::Create(
|
|
IStorage *pstg,
|
|
REFFMTID rfmtid,
|
|
const CLSID *pclsid,
|
|
DWORD grfFlags,
|
|
HRESULT *phr)
|
|
{
|
|
HRESULT & hr = *phr;
|
|
BOOL fCreated = FALSE;
|
|
STATSTG statstg;
|
|
|
|
// Save the given Storage.
|
|
|
|
_pstgPropSet = pstg;
|
|
_pstgPropSet->AddRef();
|
|
|
|
// Initialize this object. We'll assume that the STGM_READWRITE bit
|
|
// is set, even if the statstg.grfMode doesn't have it set
|
|
// (this bit doesn't get set by the memory-based IStream::Stat implementation).
|
|
|
|
|
|
hr = _pstgPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
DfpAssert( grfFlags & PROPSETFLAG_NONSIMPLE );
|
|
hr = InitializeOnCreateOrOpen( grfFlags, statstg.grfMode | STGM_READWRITE, 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(ocsContents,
|
|
( _grfAccess | _grfShare | STGM_CREATE ) & ~STGM_TRANSACTED,
|
|
0, 0, &_pstmPropSet);
|
|
if (FAILED(hr)) goto Exit;
|
|
fCreated = TRUE;
|
|
|
|
// Initialize the CONTENTS Stream.
|
|
|
|
hr = InitializePropertyStream(
|
|
CREATEPROP_CREATE,
|
|
&rfmtid,
|
|
pclsid);
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// In the transacted case, ensure that the contents
|
|
// stream is actually published right away.
|
|
|
|
if( statstg.grfMode & STGM_TRANSACTED )
|
|
{
|
|
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 = RtlFlushPropertySet(_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( ocsContents );
|
|
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::Create(IStorage*)"
|
|
" hr=%08X\n", this, hr));
|
|
}
|
|
|
|
return;
|
|
} // 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.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CPropertyStorage::Open(
|
|
IStream *pstm,
|
|
REFFMTID rfmtid,
|
|
DWORD grfFlags,
|
|
BOOL fDelete,
|
|
HRESULT *phr)
|
|
{
|
|
HRESULT & hr = *phr;
|
|
|
|
USHORT createprop = 0L;
|
|
STATSTG statstg;
|
|
|
|
// Keep a copy of the Stream.
|
|
|
|
_pstmPropSet = pstm;
|
|
_pstmPropSet->AddRef();
|
|
|
|
// Initialize this object.
|
|
|
|
hr = _pstmPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = InitializeOnCreateOrOpen( grfFlags,
|
|
statstg.grfMode,
|
|
rfmtid,
|
|
FALSE ); // => Open
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Determine the CREATEPROP flags.
|
|
|
|
if( fDelete )
|
|
{
|
|
// Only simple Sections may be deleted (really, only the
|
|
// second section of the DocumentSummaryInformation property
|
|
// set may be deleted in this way).
|
|
DfpAssert( !IsNonSimple() );
|
|
|
|
createprop = CREATEPROP_DELETE;
|
|
}
|
|
else
|
|
{
|
|
createprop = (S_OK == IsWriteable() ? CREATEPROP_WRITE : CREATEPROP_READ);
|
|
}
|
|
|
|
// Initialize the property set Stream.
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// sets up _usCodePage
|
|
hr = InitializePropertyStream(
|
|
createprop,
|
|
&rfmtid,
|
|
NULL);
|
|
|
|
}
|
|
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));
|
|
}
|
|
|
|
return;
|
|
|
|
} // 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.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CPropertyStorage::Open(
|
|
IStorage *pstg,
|
|
REFFMTID rfmtid,
|
|
DWORD grfFlags,
|
|
HRESULT *phr)
|
|
{
|
|
HRESULT & hr = *phr;
|
|
CPropSetName psn(rfmtid);
|
|
USHORT createprop = 0L;
|
|
STATSTG statstg;
|
|
|
|
|
|
// Keep a copy of the Storage
|
|
|
|
_pstgPropSet = pstg;
|
|
_pstgPropSet->AddRef();
|
|
|
|
// Initialize this object.
|
|
|
|
hr = _pstgPropSet->Stat( &statstg, STATFLAG_NONAME );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = InitializeOnCreateOrOpen( grfFlags,
|
|
statstg.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( ocsContents,
|
|
0,
|
|
(_grfAccess | _grfShare) & ~STGM_TRANSACTED,
|
|
0,
|
|
&_pstmPropSet );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// Set the CREATEPROP flags.
|
|
createprop = (S_OK == IsWriteable() ? CREATEPROP_WRITE : CREATEPROP_READ);
|
|
|
|
// Load the property set Stream.
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// sets up _usCodePage
|
|
hr = InitializePropertyStream(
|
|
createprop,
|
|
&rfmtid,
|
|
NULL);
|
|
|
|
}
|
|
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));
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
} // CPropertyStorage::Open( IStorage *, ...
|
|
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Member: CPropertyStorage::CreateMappedStream
|
|
//
|
|
// Synopsis: Create a CMappedStream object on an IStream.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: This method creates a CMappedStream which maps
|
|
// an IStream. The CDocFilePropertyStorage derivative
|
|
// overrides this member to create a CMappedStream
|
|
// on a CExposedStream.
|
|
//
|
|
//+----------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPropertyStorage::CreateMappedStream()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DfpAssert( NULL != _pstmPropSet );
|
|
DfpAssert( NULL == _ms );
|
|
|
|
_ms = new CCFMappedStream( _pstmPropSet );
|
|
if( NULL == _ms )
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
return( hr );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Member: CDocFilePropertyStorage::CreateMappedStream
|
|
//
|
|
// Synopsis: Create a CMappedStream object on a
|
|
// CExposedStream.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: This method creates a CMappedStream which maps
|
|
// a CExposedStream. I.e. it bypasses the
|
|
// IStream interface, and can access internal
|
|
// functionality (such as the tree mutex).
|
|
//
|
|
//+----------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CDocFilePropertyStorage::CreateMappedStream()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// In the DLL implementation of this code, this class can't be used,
|
|
// and causes compiler errors, so we ifdef it out.
|
|
|
|
#ifdef IPROPERTY_DLL
|
|
DfpAssert( !"CDocFilePropertyStorage used in IProp DLL" );
|
|
#else
|
|
|
|
DfpAssert( NULL != _pstmPropSet );
|
|
|
|
// Convert the underlying IStream to a CExposedStream.
|
|
CExposedStream *pexpstm = (CExposedStream*)_pstmPropSet;
|
|
DfpAssert(pexpstm->Validate() != STG_E_INVALIDHANDLE );
|
|
|
|
// Convert the CExposedStream to a CMappedStream and we're done.
|
|
_ms = (CMappedStream*)pexpstm;
|
|
|
|
PropDbg((DEB_PROP_TRACE_CREATE, "CPropertyStorage(%08X)::CreateMappedStream"
|
|
" - using CExposedDocfile as CMappedStream\n", this));
|
|
|
|
#endif
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Members: CPropertyStorage:: access control method forwarders.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
#ifdef _CAIRO_
|
|
// IAccessControl methods
|
|
STDMETHODIMP CPropertyStorage::GrantAccessRights(ULONG cCount,
|
|
ACCESS_REQUEST pAccessRequestList[])
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->GrantAccessRights(cCount, pAccessRequestList);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::SetAccessRights(ULONG cCount,
|
|
ACCESS_REQUEST pAccessRequestList[])
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->SetAccessRights(cCount, pAccessRequestList);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::ReplaceAllAccessRights(ULONG cCount,
|
|
ACCESS_REQUEST pAccessRequestList[])
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->ReplaceAllAccessRights(cCount, pAccessRequestList);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::DenyAccessRights(ULONG cCount,
|
|
ACCESS_REQUEST pAccessRequestList[])
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->DenyAccessRights(cCount, pAccessRequestList);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::RevokeExplicitAccessRights(ULONG cCount,
|
|
TRUSTEE pTrustee[])
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->RevokeExplicitAccessRights(cCount, pTrustee);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::IsAccessPermitted(TRUSTEE *pTrustee,
|
|
DWORD grfAccessPermissions)
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->IsAccessPermitted(pTrustee, grfAccessPermissions);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::GetEffectiveAccessRights(TRUSTEE *pTrustee,
|
|
DWORD *pgrfAccessPermissions )
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->GetEffectiveAccessRights(pTrustee, pgrfAccessPermissions);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::GetExplicitAccessRights(ULONG *pcCount,
|
|
PEXPLICIT_ACCESS *pExplicitAccessList)
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->GetExplicitAccessRights(pcCount, pExplicitAccessList);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::CommitAccessRights(DWORD grfCommitFlags)
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->CommitAccessRights(grfCommitFlags);
|
|
}
|
|
|
|
STDMETHODIMP CPropertyStorage::RevertAccessRights()
|
|
{
|
|
DfpAssert((_pIAC != NULL));
|
|
return _pIAC->RevertAccessRights();
|
|
}
|
|
#endif
|
|
|